backbone庫的結構:html
http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html前端
本文全部例子來自於http://blog.csdn.net/eagle_110119/article/details/8842007java
1.1 先看model塊的結構node
var Model = Backbone.Model = function(attributes, options){} _.extend(Model.prototype, Events,{..}) var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit']; _.each(modelMethods, function(method) {})
第一個是Model的構造器,第二個是Model的原型。注意,這裏將Events和一系列的自定義參數都放進了Model的原型上,backbone必須依賴一個underscore庫,咱們在underscore庫中找到相對應的方法。ajax
_.extend = function(obj) { each(slice.call(arguments, 1), function(source) { if (source) { for (var prop in source) { obj[prop] = source[prop]; } } }); return obj; };
model上的方法很是多,咱們先從實例化開始,先上例子mongodb
//定義Book模型類 var Book = Backbone.Model.extend({ defaults: { name: 'unknow', author: 'unknow', price: 0 } })
第一句話:數據庫
var Model = Backbone.Model = function(attributes, options){...} Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend
找到extend方法json
var extend = function(protoProps, staticProps) { var parent = this; var child; // The constructor function for the new subclass is either defined by you // (the "constructor" property in your `extend` definition), or defaulted // by us to simply call the parent's constructor. if (protoProps && _.has(protoProps, 'constructor')) {//檢查protoProps是否擁有constructor屬性(不考慮原型上) child = protoProps.constructor; } else { child = function(){ return parent.apply(this, arguments); };//借用構造器,this指向model構造器,讓子類實例化時,能夠獲取父類構造器的成員 } // Add static properties to the constructor function, if supplied. _.extend(child, parent, staticProps);//將父類和staticProps上的屬性成員通通傳給child的構造器上 // Set the prototype chain to inherit from `parent`, without calling // `parent`'s constructor function. var Surrogate = function(){ this.constructor = child; }; Surrogate.prototype = parent.prototype; child.prototype = new Surrogate;//臨時構造器的方式完成繼承,Surrogate屬於中間件,子類實例修改不會影響父類原型,可讓子類實例獲取父類原型上的成員 // Add prototype properties (instance properties) to the subclass, // if supplied. if (protoProps) _.extend(child.prototype, protoProps);//將自定義信息綁定到子類原型上 // Set a convenience property in case the parent's prototype is needed // later. child.__super__ = parent.prototype; //_super_屬性方便子類直接訪問父類原型 return child; //返回子類構造器 };
因此咱們最後獲得那個Book實際上是一個繼承了Model的子類構造器。ok,使用它,必需要實例化它。數組
var javabook = new Book();
這裏,咱們能夠在實例化的時候,傳入咱們的參數,以下:瀏覽器
var javabook = new Book({ name : 'Thinking in Java', author : 'Bruce Eckel', price : 395.70 })
咱們看一下構造器
var Model = Backbone.Model = function(attributes, options) { var defaults; var attrs = attributes || {}; options || (options = {}); this.cid = _.uniqueId('c');//生成惟一id this.attributes = {}; if (options.collection) this.collection = options.collection; if (options.parse) attrs = this.parse(attrs, options) || {}; options._attrs || (options._attrs = attrs);//讓options的屬性中擁有attributes,這在插件,庫中很常見的寫法 if (defaults = _.result(this, 'defaults')) {//this指向Book的實例,由於defaults對象被綁定到了Book的原型上,因此this是能夠訪問的 attrs = _.defaults({}, attrs, defaults);//合併 } this.set(attrs, options);//執行原型上的set方法 this.changed = {};//將changed(變化的)清空 this.initialize.apply(this, arguments);//實例化時執行 }
在進入set方法以前,系統會將你傳進去的參數與原型上的默認參數進行合併,不清楚的能夠看一下_.defaults方法
_.defaults = function(obj) { each(slice.call(arguments, 1), function(source) { if (source) { for (var prop in source) { console.log(obj[prop]); if (obj[prop] === void 0) obj[prop] = source[prop]; } } }); return obj; }
這裏咱們又有了一個新的判斷方法,記得在原型上的默認參數,都是unknow和0,做者這樣寫也能夠判斷,你們學習一下。通過這個過濾,留下的基本都是咱們自定義的參數了。
進入set方法。
set: function(key, val, options) { var attr, attrs, unset, changes, silent, changing, prev, current; if (key == null) return this; // Handle both `"key", value` and `{key: value}` -style arguments. if (typeof key === 'object') { attrs = key; options = val; } else { (attrs = {})[key] = val; } options || (options = {}); // Run validation. // 執行自定義的validation if (!this._validate(attrs, options)) return false;//validate爲false時不能經過 // Extract attributes and options. // 提取屬性和選項 unset = options.unset; silent = options.silent; changes = []; changing = this._changing; this._changing = true; if (!changing) { this._previousAttributes = _.clone(this.attributes); this.changed = {}; } //current表示當前狀態,prev表示上一個狀態。 current = this.attributes, prev = this._previousAttributes; // Check for changes of `id`. if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];//檢測id // For each `set` attribute, update or delete the current value. for (attr in attrs) { val = attrs[attr]; if (!_.isEqual(current[attr], val)) changes.push(attr);//將變化了屬性名加入數組 if (!_.isEqual(prev[attr], val)) { this.changed[attr] = val;//跟上此狀態不相同的,修改成如今的值 } else { delete this.changed[attr]; } unset ? delete current[attr] : current[attr] = val;//第一次賦完值後,將值放入current中。 } // Trigger all relevant attribute changes. // silent配置用於忽略驗證規則,而且它不會觸發change和error等事件 if (!silent) { if (changes.length) this._pending = true; for (var i = 0, l = changes.length; i < l; i++) { //console.log(current[changes[i]]); this.trigger('change:' + changes[i], this, current[changes[i]], options); } } // You might be wondering why there's a `while` loop here. Changes can // be recursively nested within `"change"` events. // 在全部事件結束後,觸發一次change事件 if (changing) return this; if (!silent) { while (this._pending) { this._pending = false; this.trigger('change', this, options); } } this._pending = false; this._changing = false; return this; }
驗證和silent這塊,咱們在例子上再說。set中很重要的一步就是處理當前狀態和上一個狀態,保存相應狀態。
最後,咱們執行
this.changed = {};//將changed(變化的)清空 this.initialize.apply(this, arguments);//實例化時執行
看一下原型上的initialize方法
initialize: function(){ }
英文註釋是:Initialize is an empty function by default. Override it with your own,用到的時候,咱們須要重載下。
以上完成了一個Book的實例化。
1.2 關於model實例讀取數據
看個例子:
console.log(javabook.get('name')); console.log(javabook.get('author')); console.log(javabook.get('price'));
顯示結果:
1.2.1 get方法
看一下get方法
get: function(attr) { return this.attributes[attr]; }
留心一下咱們會發現,這個this.attributes在哪出現。
current = this.attributes, prev = this._previousAttributes; // Check for changes of `id`. if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];//檢測id // For each `set` attribute, update or delete the current value. for (attr in attrs) { val = attrs[attr]; if (!_.isEqual(current[attr], val)) changes.push(attr);//將變化了屬性名加入數組 if (!_.isEqual(prev[attr], val)) { this.changed[attr] = val;//跟上此狀態不相同的,修改成如今的值 } else { delete this.changed[attr]; } unset ? delete current[attr] : current[attr] = val;//第一次賦完值後,將值放入current中。 }
將this.attributes和current之間是引用傳遞,當current[attr]的值變化時,this.attributes中的值也發生了變化,剛開始current爲一個空對象,它會根據你自定義傳入的對象,去複製過來。另外attributes是實例上的屬性,因此咱們能夠這樣取值。
console.log(javabook.attributes['name']) console.log(javabook.attributes['author']) console.log(javabook.attributes['price'])
其實結果是同樣的。
1.2.2 escape()
咱們看一下,另一種取值方法:escape()
escape: function(attr) { return _.escape(this.get(attr)); }
看似作了層過濾,看下_.escape方法
_.each(['escape', 'unescape'], function(method) { _[method] = function(string) { if (string == null) return '';//爲空將返回 return ('' + string).replace(entityRegexes[method], function(match) {//匹配到正則的部分執行function方法,實際上就是將<,>,&,",'等進行轉換 return entityMap[method][match]; }); }; }); var entityRegexes = { escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),//拼正則 unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') }; var entityMap = {//須要轉換的部分 escape: { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' } };
若是須要添加新的轉換部分,能夠添加到entityMap中。
1.3 修改數據
看例子:
var javabook = new Book(); // 經過set方法設置模型數據 javabook.set('name', 'Java7入門經典'); javabook.set('author', 'Ivor Horton'); javabook.set('price', 88.50); // 獲取數據並將數據輸出到控制檯 var name = javabook.get('name'); var author = javabook.get('author'); var price = javabook.get('price'); console.log(name); // 輸出Java7入門經典 console.log(author); // 輸出Ivor Horton console.log(price); // 輸出88.50
原型上的set方法,剛纔咱們已經看過了,set方法會將咱們傳入的值與上一次狀態的值進行比較,一樣也與沒賦此次值的當前值進行比較。若是改變了,則將新的值覆蓋舊的值,不過this._previousAttributes中保存着model上一次狀態的值。
set能夠單獨一個個賦值,一樣也能夠一塊兒賦值
//set()方法也容許同時設置多個屬性,例如: javabook.set({ name : 'Java7入門經典', author : 'Ivor Horton', price : 88.50 });
1.4 修改數據嗎,觸發事件
當調用set()方法修改模型中的數據時,會觸發一系列事件,咱們經常經過監聽這些事件,來動態調整界面中數據的顯示,咱們先來看一個例子:
// 定義Book模型類 var Book = Backbone.Model.extend({ defaults : { name : 'unknown', author : 'unknown', price : 0 } }); // 實例化模型對象 var javabook = new Book(); // 監聽模型"change"事件 javabook.on('change', function(model) { console.log('change事件被觸發'); }); // 監聽模型"change:name"事件 javabook.on('change:name', function(model, value) { console.log('change:name事件被觸發'); }); // 監聽模型"change:author"事件 javabook.on('change:author', function(model, value) { console.log('change:author事件被觸發'); }); // 經過set()方法設置數據 javabook.set({ name : 'Thinking in Java', author : 'unknown', price : 395.70 }); // 控制檯輸出結果: // change:name事件被觸發 // change事件被觸發
問題在set方法中,通常狀況咱們不設置slient的狀況下,會執行事件,看代碼:
// silent配置用於忽略驗證規則,而且它不會觸發change和error等事件 if (!silent) { if (changes.length) this._pending = true; for (var i = 0, l = changes.length; i < l; i++) { this.trigger('change:' + changes[i], this, current[changes[i]], options); } }
若是咱們修改的數據,changes數組中是會有值的。遍歷changes數組,將修改過的屬性名找到,執行相似'change:屬性名'爲名稱的事件,這裏,告訴咱們在頁面,若是想經過數據修改觸發事件的話,這個事件的命名按照'change'+'屬性名'來定義。
另外,set源碼中,還有一段:
// 在全部事件結束後,觸發一次change事件 if (changing) return this; if (!silent) { while (this._pending) { this._pending = false; this.trigger('change', this, options); } }
一旦數據不真正修改了,那this._pending將變爲true,將默認執行一遍change方法。咱們可能監聽change方法來判斷數據是否被修改(可是你也能夠經過獲取實例的_pending屬性來判斷)
除了get方法以外,還有兩種方法獲取上一次狀態的值
previous()
previousAttributes()
先看previous()
previous: function(attr) { if (attr == null || !this._previousAttributes) return null; return this._previousAttributes[attr]; }
很簡單,this._previousAttributes存放着上一個狀態的參數。
previousAttributes()
previousAttributes: function() { return _.clone(this._previousAttributes); }
克隆一份返回,這樣修改不會影響原來的狀態值。
1.5 數據驗證
Backbone模型提供了一套數據驗證機制,確保咱們在模型中存儲的數據都是經過驗證的,咱們經過下面的例子來講明這套驗證機制:
var Book = Backbone.Model.extend({ validate : function(data) { if(data.price < 1) { return '書籍價格不該低於1元.'; } } }); var javabook = new Book(); // 監聽error事件,當驗證失敗時觸發 javabook.on('error', function(model, error) { console.log(error); }); javabook.set('price', 0);
找到set方法中的相應方法:
if (!this._validate(attrs, options)) return false;
你們注意,上述的例子沒有做用,爲何?由於this._validate()中傳入的兩個參數爲空,定義Book時傳入的validate實際上綁定到Book的原型上。實例化時根本沒有傳入任何數據。這裏源碼存在錯誤,看看咱們該如何修改。
先看_validate方法:
_validate: function(attrs, options) { //if (!options.validate || !this.validate) return true;//這裏的this.validate是你本身定義的,因此validate須要定義在model類中 if(!this.validate || !options.validate) return true//沒有驗證,直接經過
attrs = _.extend({}, this.attributes, attrs); var error = this.validationError = this.validate(attrs, options) || null; if (!error) return true;//沒有報錯內容,返回true
this.trigger('invalid', this, error, _.extend(options, {validationError: error})); //若是是false,則表示驗證正確,不然則自動執行下面的trigger方法,拋出異常 return false; }
由於options沒值,因此!options.validate恆爲true,這也就是爲何validate不驗證的關鍵。修改下判斷爲:
if(!this.validate || !options.validate) return true//沒有驗證,直接經過
繼續向下,看這段代碼:
this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
若是拋出錯誤,會執行事件名爲invalid的事件,那咱們再看看頁面的綁定事件名,是error,不相符,致使從this._events中按事件名取事件取不到,致使驗證失敗。ok,簡單修改下頁面
// 監聽error事件,當驗證失敗時觸發 javabook.on('invalid', function(model, error) { console.log(error); });
ok,修改完成,咱們再運行一遍,看結果
對於修改validate這塊,我只是列舉了一種辦法,還有不少方法,你選擇你喜歡。再說一句,綁定validate還有一種方式:
javabook.set('price', 0, { error : function(model, error) { console.log('自定義錯誤:' + error); } });
options將會有值,這樣就不會跳出validate判斷了。
1.6 slient配置
這個東西擱了一段沒看,如今咱們根據例子來過一遍。上例子:
javabook.set('price', 0, { silent : true });
若是傳入silent,那將不觸發change和change:屬性名的事件,但記住,它只在定義它的時候生效,換言之,以下代碼:
javabook.set('price', 0, { silent : true }); javabook.set('name', 'Thinking in Java');
第一次set不會拋異常,第二次會,爲何,由於傳入的參數不同。第二次沒有silent,就能夠觸發change等事件了。
1.7 刪除數據
Backbone提供了unset和clear方法,看下API
unset()方法用於刪除對象中指定的屬性和數據
clear()方法用於刪除模型中全部的屬性和數據
例子:
// 定義Book模型類 var Book = Backbone.Model.extend(); // 實例化模型對象 var javabook = new Book({ name : 'Java7入門經典', author : 'Ivor Horton', price : 88.50 }); // 輸出: Java7入門經典 console.log(javabook.get('name')); // 刪除對象name屬性 javabook.unset('name'); // 輸出: undefined console.log(javabook.get('name')); 當咱們對模型的name屬性執行unset()方法後,模型內部會使用delete關鍵字將name屬性從對象中刪除。 clear()方法與unset()方法執行過程相似,但clear()方法會刪除模型中的全部數據,例如: // 定義Book模型類 var Book = Backbone.Model.extend(); // 實例化模型對象 var javabook = new Book({ name : 'Java7入門經典', author : 'Ivor Horton', price : 88.50 }); // 刪除對象name屬性 javabook.clear(); // 如下均輸出: undefined console.log(javabook.get('name')); console.log(javabook.get('author')); console.log(javabook.get('price'));
先從unset開始
javabook.unset('name');
找到相應的unset方法
unset: function(attr, options) { return this.set(attr, void 0, _.extend({}, options, {unset: true})); }
能夠看的很清楚,unset仍是用到set方法,注意它傳入的{unset:true}的,回到以前的set方法中,去找相應的unset部分,咱們會找到這段代碼。
unset ? delete current[attr] : current[attr] = val;//第一次賦完值後,將值放入current中。
unset爲true以後,delete current[attr],即刪除current['name']。表示刪除了name屬性。
再來看clear方法
clear: function(options) { var attrs = {}; for (var key in this.attributes) attrs[key] = void 0;//將this.attributes中全部的成員清空,這裏undefined可能會被重寫,因此用void 0代替 return this.set(attrs, _.extend({}, options, {unset: true})); }
clear方法依舊是調用set方法,因爲成員被清空,再傳入set中,刪除掉current[attr],釋放掉內存。
1.8 將模型數據同步到服務器
1.8.1 save
Backbone提供了與服務器數據的無縫鏈接,咱們只須要操做本地Model對象,Backbone就會按照規則自動將數據同步到服務器。若是須要使用Backbone默認的數據同步特性,請肯定你的服務器數據接口已經支持了REST架構。具體概念,你們能夠看http://blog.csdn.net/eagle_110119/article/details/8842007部分的講解,或者上網搜些資料看。
數據標識:Backbone中每個模型對象都有一個惟一標識,默認名稱爲id,你能夠經過idAttribute屬性來修改它的名稱。
URL: Backbone默認使用PATHINFO的方式來訪問服務器接口。
先看例子
// 定義Book模型類 var Book = Backbone.Model.extend({ urlRoot : '/service' }); // 建立實例 var javabook = new Book({ id : 1001, name : 'Thinking in Java', author : 'Bruce Eckel', price : 395.70 }); // 保存數據 javabook.save();
看一下save方法
save: function(key, val, options) { var attrs, method, xhr, attributes = this.attributes; // Handle both `"key", value` and `{key: value}` -style arguments. if (key == null || typeof key === 'object') { attrs = key; options = val; } else { (attrs = {})[key] = val; } options = _.extend({validate: true}, options); // If we're not waiting and attributes exist, save acts as // `set(attr).save(null, opts)` with validation. Otherwise, check if // the model will be valid when the attributes, if any, are set. if (attrs && !options.wait) { if (!this.set(attrs, options)) return false; } else { if (!this._validate(attrs, options)) return false;//驗證經過了 } // Set temporary attributes if `{wait: true}`. if (attrs && options.wait) { this.attributes = _.extend({}, attributes, attrs); } // After a successful server-side save, the client is (optionally) // updated with the server-side state. if (options.parse === void 0) options.parse = true; 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); if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { return false; } if (success) success(model, resp, options); model.trigger('sync', model, resp, options); }; wrapError(this, options);//將options綁定一個error方法 method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');//判斷id,若是沒有則新建,若是有再判斷options.patch,改成更新 if (method === 'patch') options.attrs = attrs; xhr = this.sync(method, this, options); // Restore attributes. if (attrs && options.wait) this.attributes = attributes; return xhr; }
這裏,咱們先只從前端的角度看save方法,有條件的朋友,能夠搭建一個環境,與服務器交互下,效果會更好(目前我也再搞,我使用的是node+mongodb)
在save方法中,咱們調用了validate進行了驗證。驗證不經過,則不容許發送請求。其中的options.success是一個返回成功的回調函數。看一下wrapError
var wrapError = function(model, options) { var error = options.error; options.error = function(resp) { if (error) error(model, resp, options); model.trigger('error', model, resp, options); }; };
這個方法,給options添加了一個error方法,主要是爲了防止當ajax請求失敗時,捕獲錯誤信息。
其中ajax的請求,主要包含:url,method,async,datatype,success,error等。看一下save若是處理method
method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');//判斷id,若是沒有則新建,若是有再判斷options.patch,改成更新
接着是
xhr = this.sync(method, this, options);
ajax歸根結底是經過XMLHttpRequest實例來操做的,來看如何建立一個xhr實例,進入原型的sync方法
sync: function() { return Backbone.sync.apply(this, arguments); }
看一下Backbone.sync
Backbone.sync = function(method, model, options) { var type = methodMap[method]; // Default options, unless specified. _.defaults(options || (options = {}), { emulateHTTP: Backbone.emulateHTTP,//false emulateJSON: Backbone.emulateJSON //false }); // Default JSON-request options. // 默認JSON請求選項 var params = {type: type, dataType: 'json'}; // Ensure that we have a URL. // 查看選項中是否有url,沒有則選用實例set時的url if (!options.url) { params.url = _.result(model, 'url') || urlError(); } // Ensure that we have the appropriate request data. if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { params.contentType = 'application/json';//用於定義網絡文件的類型和網頁的編碼,決定瀏覽器將以什麼形式、什麼編碼讀取這個文件 params.data = JSON.stringify(options.attrs || model.toJSON(options));//轉成字符串,其中model.toJSON()返回this.attributes裏的信息 } // params中包含了發送給服務器的全部信息 // For older servers, emulate JSON by encoding the request into an HTML-form. if (options.emulateJSON) { params.contentType = 'application/x-www-form-urlencoded'; params.data = params.data ? {model: params.data} : {}; } // For older servers, emulate HTTP by mimicking the HTTP method with `_method` // And an `X-HTTP-Method-Override` header. if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { params.type = 'POST'; if (options.emulateJSON) params.data._method = type; var beforeSend = options.beforeSend; options.beforeSend = function(xhr) { xhr.setRequestHeader('X-HTTP-Method-Override', type); if (beforeSend) return beforeSend.apply(this, arguments); }; } // Don't process data on a non-GET request. if (params.type !== 'GET' && !options.emulateJSON) { params.processData = false; } // If we're sending a `PATCH` request, and we're in an old Internet Explorer // that still has ActiveX enabled by default, override jQuery to use that // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8. if (params.type === 'PATCH' && noXhrPatch) { params.xhr = function() { return new ActiveXObject("Microsoft.XMLHTTP"); }; } // Make the request, allowing the user to override any Ajax options. var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); model.trigger('request', model, xhr, options); return xhr; }
其中,整個這個方法中,主要完成的工做,就是填充params,讓其包含傳到服務器所須要的全部信息,包括頭,編碼等等。另外在ajax中存在兼容性問題,低版本的IE沒有xhr對象,它們有本身的實例對象activeObject。
兼容性判斷
var noXhrPatch = typeof window !== 'undefined' && !!window.ActiveXObject && !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
瀏覽器不支持xhr,可使用activeObject
代碼最後,調用了Backbone.ajax(_.extend(params,options))
Backbone.ajax = function() { return Backbone.$.ajax.apply(Backbone.$, arguments); }
再看
Backbone.$ = root.jQuery || root.Zepto || root.ender || root.$;
這裏,咱們都清楚了,Backbone在與服務器交互時,再發送請求時調用的是第三方的ajax,這裏咱們使用的是jQuery。最後返回xhr對象。save方法結束。(這裏你們能夠結合$.ajax()來理解)
例子中還將,能夠這樣寫回調
// 將數據保存到服務器 javabook.save(null, { success : function(model) { // 數據保存成功以後, 修改price屬性並從新保存 javabook.set({ price : 388.00 }); javabook.save(); } });
這樣也能夠,爲何呢,看源碼:
var success = options.success;//你能夠在save方法的時候寫成功回調 options.success = function(resp) {//返回成功的回調 // Ensure attributes are restored during synchronous saves. model.attributes = attributes; var serverAttrs = model.parse(resp, options); if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { return false; } if (success) success(model, resp, options); model.trigger('sync', model, resp, options); };
沒有本身寫回調,系統幫你寫,若是有回調,則使用你的回調,至於回調函數,咱們最後看。
還有一個wait參數的配置,看例子
// 從將數據保存到服務器 javabook.save({ name : 'Thinking in Java', author : 'Bruce Eckel', price : 395.70 }, { wait : true });
例子中的解答是若是咱們傳遞了wait配置爲true,那麼數據會在被提交到服務器以前進行驗證,當服務器沒有響應新數據(或響應失敗)時,模型中的數據會被還原爲修改前的狀態。若是沒有傳遞wait配置,那麼不管服務器是否保存成功,模型數據均會被修改成最新的狀態、或服務器返回的數據。
咱們來看回調,裏面有幾個方法
if (attrs && !options.wait) { if (!this.set(attrs, options)) return false; } else { if (!this._validate(attrs, options)) return false;//驗證經過了 }
若是設置了wait爲true,將會進行驗證(其實你不傳值,也要進行驗證。。。)
再看這個方法
parse: function(resp, options) { return resp; }
對照save裏的方法
model.attributes = attributes; var serverAttrs = model.parse(resp, options); if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
若是設置了wait,當服務器沒有響應時,理論上resp是沒有值,serverAttrs的值應該爲attrs,這個是原來修改前的值。對於ajax請求失敗,失敗了服務器通常返回錯誤信息,數據庫裏的數據是不會修改原來的狀態,因此原來的狀態依舊是原來的狀態。
其實在咱們不設置wait參數時,save方法能夠有參數,不須要在此以前使用set,由於他包含了這set方法看例子
javabook.save({ name: 'helloWorld' //,wait: true } );
運行結果爲:
能夠看到name被修改了,save完成了set的功能,前提,不要設置wait,wait能夠理解爲保持原有的參數不變(在ajax沒有返回時,或者報錯時)
不少時候,接口返回的數據是多種多樣的,例子上有一種狀況
{ "resultCode" : "0", "error" : "null", "data" : [{ "isNew" : "true", "bookId" : "1001", "bookName" : "Thinking in Java(修訂版)", "bookAuthor" : "Bruce Eckel", "bookPrice" : "395.70" }] }
backbone提供了一個parse方法,不過以前咱們已經看過,默認狀況,這個方法提供傳入兩個參數,並返回第一個參數,解析時,本身重寫parse方法解析。
1.8.2 fetch
fetch()方法用於從服務器接口獲取模型的默認數據,經常用於模型的數據恢復,它的參數和原理與save()方法相似,所以你能夠很容易理解它。
從fetch代碼看起:
fetch: function(options) { options = options ? _.clone(options) : {}; if (options.parse === void 0) options.parse = true; var model = this; var success = options.success; options.success = function(resp) {//成功的回調 if (!model.set(model.parse(resp, options), options)) return false; if (success) success(model, resp, options); model.trigger('sync', model, resp, options); }; wrapError(this, options);//錯誤的回調 return this.sync('read', this, options); }
基本跟save類似,注意,這裏的標識爲read。若是須要在回調中綁定響應事件的,能夠在頁面用on綁定事件,事件名爲sync,這樣是正確回調後,是能夠觸發的。
這裏,由於沒有鏈接上服務器,因此id的部分沒有給出,抱歉。
1.8.3 destroy
destroy()方法用於將數據從集合(關於集合咱們將在下一章中討論)和服務器中刪除,須要注意的是,該方法並不會清除模型自己的數據,若是須要刪除模型中的數據,請手動調用unset()或clear()方法)當你的模型對象從集合和服務器端刪除時,只要你再也不保持任何對模型對象的引用,那麼它會自動從內存中移除。(一般的作法是將引用模型對象的變量或屬性設置爲null值)
看一下destory
destroy: function(options) { options = options ? _.clone(options) : {}; var model = this; var success = options.success; var destroy = function() { //模型會觸發destroy事件,頁面須要聲明 model.trigger('destroy', model, model.collection, options); }; options.success = function(resp) { //成功回調 if (options.wait || model.isNew()) destroy(); if (success) success(model, resp, options); if (!model.isNew()) model.trigger('sync', model, resp, options); }; if (this.isNew()) {//查看id是不是新的。 options.success(); return false; } wrapError(this, options);//錯誤回調 var xhr = this.sync('delete', this, options); if (!options.wait) destroy();//設置wait以後,將不會觸發destory return xhr; }
這裏的標識爲delete,能夠看到,該方法並不會清除模型自己(也就是沒有跟this.attributes打交道)。
這裏基本過完一遍model。ok,好吧有點長。。
內容很少,時間恰好,以上是個人一點讀碼體會,若有錯誤,請指出,你們共通學習。