簡(jiǎn)單了解Backbone.js的Model模型以及View視圖的源碼
Backbone.Model
今天我們先來(lái)談?wù)凚ackbone.js MVC 中的 M , Model是backbone的核心部分,包含著頁(yè)面展示內(nèi)容的數(shù)據(jù),還有圍繞著數(shù)據(jù)操作的各種 轉(zhuǎn)換,校驗(yàn),計(jì)算 ,權(quán)限控制,服務(wù)端交互等等操作,你可以通過(guò) Backbone.Model.extend() 生成你的model , 當(dāng)然生成的model也可以作為一個(gè)基類去向下擴(kuò)展更多的model
var People = Backbone.Model.extend({ }); var Man = People.extend({ });
Backbone.Model Api
Backbone.Model 提供了大量方法用于實(shí)現(xiàn)一個(gè)Model的基本操作,當(dāng)然其中最基本的還是基于 Backbone.Events 的事件機(jī)制,在Model的attributes發(fā)生變化的時(shí)候,相應(yīng)的 change:attr事件會(huì)被觸發(fā),下面是提供的API:
其中有對(duì)數(shù)據(jù)進(jìn)行服務(wù)端操作的方法:
- sync : 包裝了Backbone.sync,xhr的基類
- fetch : 用于從服務(wù)端獲取數(shù)據(jù)
- save : 向服務(wù)端持久化數(shù)據(jù)
- destroy: 從服務(wù)端刪除數(shù)據(jù)
model中數(shù)據(jù)操作的方法:
- get : 從attributes中獲取數(shù)據(jù)
- set : 向attributes中設(shè)置數(shù)據(jù)
- escape : 對(duì)數(shù)據(jù)進(jìn)行編碼 ,使用的是underscore的 _.escape
- has : attributes中有無(wú)對(duì)應(yīng)數(shù)據(jù)
- unset : 從attributes中刪除數(shù)據(jù)
- clear : 清空attributes數(shù)據(jù)
- changed : 與上個(gè)狀態(tài)(執(zhí)行過(guò)set,unset),相比變化的值
- toJSON : 將 attributes 序列化成一個(gè)對(duì)象
- parse : 當(dāng)設(shè)置項(xiàng)parse為真的時(shí)候,初始化/set/unset/fetch等數(shù)據(jù)操作中會(huì)對(duì)目標(biāo)數(shù)據(jù)進(jìn)行一個(gè)解析返回解析后的對(duì)象,此方法為空方法,需要重寫覆蓋
- hasChanged : 與上個(gè)狀態(tài)(執(zhí)行過(guò)set,unset),相比是否發(fā)生過(guò)變化
- changeAttributes : 與上個(gè)狀態(tài)(執(zhí)行過(guò)set,unset),相比發(fā)生的所有值
- previous : 前一狀態(tài) (執(zhí)行過(guò)set,unset),該屬性對(duì)應(yīng)的值
- previousAttributes : 與上個(gè)狀態(tài)(執(zhí)行過(guò)set,unset),發(fā)生過(guò)變化對(duì)象的前一個(gè)狀態(tài)的所有值
model中數(shù)據(jù)校驗(yàn)的方法:
- validate:用于對(duì)model中數(shù)據(jù)進(jìn)行校驗(yàn),需要重寫覆蓋默認(rèn)方法
- validationError : 返回最近一個(gè)invalid時(shí)返回的值
- isValid : 調(diào)用_validate方法
下面會(huì)針對(duì)一些重點(diǎn)的api進(jìn)行講解:
構(gòu)造函數(shù)
var Model = Backbone.Model = function(attributes, options) { var attrs = attributes || {}; options || (options = {}); this.cid = _.uniqueId('c'); this.attributes = {}; if (options.collection) this.collection = options.collection; if (options.parse) attrs = this.parse(attrs, options) || {}; attrs = _.defaults({}, attrs, _.result(this, 'defaults')); this.set(attrs, options); this.changed = {}; this.initialize.apply(this, arguments); };
構(gòu)造函數(shù)主要對(duì)初始化的數(shù)據(jù)和選項(xiàng)進(jìn)行設(shè)置,然后會(huì)對(duì)生成一個(gè)唯一的cid用于標(biāo)示model,如果options中的parse為true,那么會(huì)對(duì)初始化數(shù)值通過(guò)parse方法進(jìn)行一個(gè)解析,調(diào)用set方法,所有的初始值會(huì)被存入attributes中,調(diào)用initialize初始化方法 。model的初始化就完成了。
set
model.set(attributes, [options])
set方法會(huì)將值設(shè)置進(jìn)入attribute中,設(shè)置時(shí)如果設(shè)置了slient為true,會(huì)觸發(fā)相應(yīng)的 change:attr 的事件,最后統(tǒng)一觸發(fā)change事件,set方法部分代碼如下:
set: function(key, val, options) { //...... // key值可以是鍵值對(duì),也可以是一個(gè)字符串,將賦值傳入attrs屬性中 if (typeof key === 'object') { attrs = key; options = val; } else { (attrs = {})[key] = val; } // .... //對(duì)設(shè)置的值進(jìn)行校驗(yàn) if (!this._validate(attrs, options)) return false; unset = options.unset; // unset為true時(shí)會(huì)刪除設(shè)置的值,unset方法就是通過(guò) set(key,val,{unset:true})去實(shí)現(xiàn)的 //當(dāng)對(duì)象正在被被設(shè)置的時(shí)候,不給 previousAttributes 賦值 if (!changing) { this._previousAttributes = _.clone(this.attributes); this.changed = {}; } current = this.attributes, prev = this._previousAttributes; //如果對(duì)Id進(jìn)行了設(shè)置,則對(duì)對(duì)象的id屬性也進(jìn)行改變 if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; //進(jìn)行 設(shè)置或者是刪除操作 for (attr in attrs) { val = attrs[attr]; if (!_.isEqual(current[attr], val)) changes.push(attr); if (!_.isEqual(prev[attr], val)) { this.changed[attr] = val;//為model的changed進(jìn)行設(shè)置 } else { delete this.changed[attr]; } unset ? delete current[attr] : current[attr] = val;//如果unset被設(shè)置成true了,則進(jìn)行刪除操作 } //在silent不為false 的情況下,進(jìn)行change:attr事件發(fā)送 if (!silent) { if (changes.length) this._pending = options; for (var i = 0, l = changes.length; i < l; i++) { this.trigger('change:' + changes[i], this, current[changes[i]], options); } } //觸發(fā)change事件 if (changing) return this; if (!silent) { while (this._pending) { options = this._pending; this._pending = false; this.trigger('change', this, options); } } this._pending = false; this._changing = false; return this; }
set的整個(gè)流程就是 對(duì)傳入的數(shù)值進(jìn)行處理,變成一個(gè)鍵值對(duì),然后對(duì)數(shù)值進(jìn)行校驗(yàn),檢查正確性,然后開(kāi)始進(jìn)行設(shè)置操作,設(shè)置時(shí)檢查數(shù)值時(shí)候是發(fā)生改變的,如果有則加入一個(gè) changeed的對(duì)象中,然后檢查unset的值,進(jìn)行相應(yīng)的添加更新刪除操作。然后依次觸發(fā)change:attr和change事件。
save
model.save([attributes], [options])
save方法用于向客戶端持久化數(shù)據(jù),會(huì)根據(jù)數(shù)據(jù)的不同和配置的不同選擇使用create,update或者是patch,并且觸發(fā) sync 事件,以下為部分代碼:
save: function(key, val, options) { // ...... //當(dāng)設(shè)置了wait屬性true的時(shí)候 , save方法先不執(zhí)行set方法(不觸發(fā)change事件),只執(zhí)行validate if (attrs && !options.wait) { if (!this.set(attrs, options)) return false; } else { if (!this._validate(attrs, options)) return false; } //如果wait為true,設(shè)置this.attributes if (attrs && options.wait) { this.attributes = _.extend({}, attributes, attrs); } // ..... var model = this; var success = options.success; options.success = function(resp) { // Ensure attributes are restored during synchronous saves. model.attributes = attributes; var serverAttrs = model.parse(resp, options); //如果wait為true , 那么會(huì)在請(qǐng)求返回之后才進(jìn)行set操作 if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { return false; } if (success) success(model, resp, options); //觸發(fā) sync 事件 model.trigger('sync', model, resp, options); }; //生成XHR onerror 回調(diào)函數(shù) wrapError(this, options); //選擇方法 method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); if (method === 'patch') options.attrs = attrs; xhr = this.sync(method, this, options); // Restore attributes. if (attrs && options.wait) this.attributes = attributes; //返回xhr對(duì)象 return xhr; }
save 中最需要注意的就是 wait 的設(shè)置,當(dāng)wait為真的時(shí)候,save返回會(huì)在xhr返回之后再執(zhí)行set操作,而不是在xhr之前就進(jìn)行set操作,因此change事件的觸發(fā)時(shí)機(jī)也就不同了。
之前說(shuō)過(guò)整個(gè)Backbone都是通過(guò)事件串聯(lián)起來(lái)的,所以對(duì)于事件觸發(fā)時(shí)機(jī)的了解和把握是非常重要的,不然會(huì)在開(kāi)發(fā)過(guò)程中導(dǎo)致一些奇怪的問(wèn)題出現(xiàn)。
Backbone.View
前面已經(jīng)對(duì)backbone中的Event、Model、Collection代碼進(jìn)行了分析,現(xiàn)在我們來(lái)看下MVC中的V部分,也就是Backbone.View,View在Backbone中主要用于溝通頁(yè)面中的DOM和Backbone.Model/Collection,頁(yè)面的邏輯操作,DOM事件的綁定等,View部分的代碼非常簡(jiǎn)答,加上注釋只有110左右。 View部分有一下API:
方法不多,下面對(duì)部分API進(jìn)行介紹:
構(gòu)造方法
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; var View = Backbone.View = function(options) { this.cid = _.uniqueId('view'); options || (options = {}); _.extend(this, _.pick(options, viewOptions)); this._ensureElement(); this.initialize.apply(this, arguments); this.delegateEvents(); };
構(gòu)造方法中為View生成了一個(gè)唯一的cid,以'view'開(kāi)頭,然后進(jìn)行對(duì)目標(biāo)屬性viewOptions進(jìn)行合并,接著調(diào)用_ensureElement判斷el的情況,接著調(diào)用delegateEvents進(jìn)行方法綁定,初始化完成 。
delegateEvents
view.setElement(element) setElement: function(element, delegate) { if (this.$el) this.undelegateEvents();//如果已經(jīng)存在this.$el,進(jìn)行事件解綁 //對(duì)$el進(jìn)行賦值,本質(zhì)是一個(gè)jquery或者是 Lo-Dash and Zepto 對(duì)象 this.$el = element instanceof Backbone.$ ? element : Backbone.$(element); //把dom element 賦值給el this.el = this.$el[0]; //如果沒(méi)有顯式傳值,則進(jìn)行事件綁定 if (delegate !== false) this.delegateEvents(); return this; }
setElement方法用于設(shè)置View對(duì)應(yīng)的element , 這個(gè)方法在new的時(shí)候會(huì)被調(diào)用, 如果想要在使用過(guò)程中改變View的dom元素指向,可調(diào)用這個(gè)方法進(jìn)行重新設(shè)置
_ensureElement
_ensureElement: function() { //如果已經(jīng)對(duì)el進(jìn)行設(shè)置,直接調(diào)用setElement方法 if (!this.el) {//如果沒(méi)有設(shè)置,生成一個(gè)元素對(duì)象,再調(diào)用setElement方法 var attrs = _.extend({}, _.result(this, 'attributes')); if (this.id) attrs.id = _.result(this, 'id'); if (this.className) attrs['class'] = _.result(this, 'className'); var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs); this.setElement($el, false); } else { this.setElement(_.result(this, 'el'), false); } }
_ensureElement這個(gè)方法是內(nèi)部方法,在構(gòu)造函數(shù)中使用,用于判斷指定的el在頁(yè)面中存不存在,如果存在則對(duì)$el進(jìn)行賦值,如果不存在,則生成一個(gè)$el,但是要注意這個(gè)對(duì)象是沒(méi)有落地到dom樹(shù)中的 。
delegateEvents
delegateEvents([events]) // *{"event selector": "callback"}* // // { // 'mousedown .title': 'edit', // 'click .button': 'save', // 'click .open': function(e) { ... } // } delegateEvents: function(events) { //如果不存在events,則直接返回 if (!(events || (events = _.result(this, 'events')))) return this; //先解除所有的事件綁定 this.undelegateEvents(); //處理每個(gè)事件 for (var key in events) { var method = events[key]; //解析回調(diào)函數(shù) if (!_.isFunction(method)) method = this[events[key]]; if (!method) continue; //對(duì)選擇器進(jìn)行分析 var match = key.match(delegateEventSplitter); var eventName = match[1], selector = match[2]; method = _.bind(method, this); //綁定的事件名都是以 eventName + '.delegateEvents' + cid 組成, //這么做能夠在undelegateEvents的時(shí)候選擇到這個(gè)View的所有事件 eventName += '.delegateEvents' + this.cid; if (selector === '') { this.$el.on(eventName, method); } else { this.$el.on(eventName, selector, method); } } return this; }
在View中你可以使用一個(gè) key:value 集合指定對(duì)應(yīng)的事件,在初始化的時(shí)候構(gòu)造函數(shù)會(huì)調(diào)用delegateEvents進(jìn)行綁定,需要注意的是所有在key中指定的元素的父元素都必須是$el,也就是說(shuō)元素必須是$el的子節(jié)點(diǎn),否則綁定失敗。
View和其他backbone模塊一個(gè)區(qū)別就是沒(méi)有自己的內(nèi)建自定義事件,當(dāng)然他也組合了Events模塊,但是所有的事件都需要自己進(jìn)行建立。View主要是一個(gè)MVC模型的承載,其實(shí)真正的功能不多,其實(shí)從模型層面上看V在前端開(kāi)發(fā)中是最接近業(yè)務(wù)邏輯的,所以在View中大部分的邏輯都是開(kāi)發(fā)者自己去擴(kuò)展的。
相關(guān)文章
深入解析Backbone.js框架的依賴庫(kù)Underscore.js的作用
這篇文章主要介紹了深入解析Backbone.js框架的依賴庫(kù)Underscore.js的作用,用過(guò)Node.js的朋友對(duì)Underscore一定不會(huì)陌生:)需要的朋友可以參考下2016-05-05JavaScript的Backbone.js框架的一些使用建議整理
這篇文章主要介紹了JavaScript的Backbone.js框架的一些使用建議整理,文中列的幾點(diǎn)主要還是針對(duì)DOM方面的操作,需要的朋友可以參考下2016-02-02簡(jiǎn)單了解Backbone.js的Model模型以及View視圖的源碼
這篇文章主要簡(jiǎn)單介紹了Backbone.js的Model模型以及View視圖的源碼,Backbone是一款高人氣JavaScript的MVC框架,需要的朋友可以參考下2016-02-02Backbone中View之間傳值的學(xué)習(xí)心得
Backbone中的View就是用來(lái)展示由Model層傳出的數(shù)據(jù),或者在View里產(chǎn)生的一些數(shù)據(jù),本文給大家介紹Backbone中View之間傳值的解決方法,感興趣的朋友一起看下吧2016-08-08輕量級(jí)javascript 框架Backbone使用指南
這篇文章主要介紹了輕量級(jí)javascript 框架Backbone使用指南的相關(guān)資料,需要的朋友可以參考下2015-07-07

全面解析JavaScript的Backbone.js框架中的Router路由

實(shí)例講解JavaScript的Backbone.js框架中的View視圖