前端MVC框架Backbone 1.1.0源碼分析(二) - 模型

模型是什麼?

Models are the heart of any JavaScript application, containing the interactive data as well as a large part of the logic surrounding it: conversions, validations, computed properties, and access control. You extend Backbone.Model with your domain-specific methods, and Model provides a basic set of functionality for managing changes.

模型 是全部 Javascript 應用程序的核心,包括交互數據及相關的大量邏輯: 轉換、驗證、計算屬性和訪問控制。css

你能夠用特定的方法擴展 Backbone.Model模型 也提供了一組基本的管理變化的功能,這個東西就像是後端開發中的數據庫映射那個model同樣,也是數據對象的模型,而且應該是和後端的model有相同的屬性(僅是須要經過前端來操做的屬性)。html

簡而言之,就是圍繞着數據處理,如建立、校驗、銷燬和保存到服務端等等...前端

 


如何設計模型

以前說了,模型能夠圍繞數據處理相似curd的操做,因此backbone就爲咱們提供了這樣的一個基礎模板,Backbone中的模型類是Backbone.Model,它包含了數據存儲,數據驗證,以及數據發生變更時觸發相關動做,咱們只要繼承就能使用這些特性了數據庫

用別人的框架,就須要瞭解別人的規則,這種學習成本是跑不掉的 - -後端

官方的demo服務器

下面是一個示例,它演示了定義一個模型使用一個自定義的方法,設置一個屬性,觸發一個事件的特定屬性的變化app

var Sidebar = Backbone.Model.extend({
    promptColor: function() {
        var cssColor = prompt("Please enter a CSS color:");
        this.set({color: cssColor});
    }
});

window.sidebar = new Sidebar;

sidebar.on('change:color', function(model, color) {
    console.log('修改顏色',color)
});

sidebar.set({color: 'white'});

sidebar.promptColor();

當models中值被改變時自動觸發一個"change"事件、全部用於展現models數據的views都會偵聽到這個事件,而後進行從新渲染。框架

Backbone.Model 是Backbone提供模板類,經過繼承extend構造本身Sidebar模型類dom

因此具備了on ,set 等等這種基礎的屬性與方法ide

 


Backbone.Model

模型構造器

那麼我看看backbone模型類模板能爲咱們提供什麼基礎功能

  1. 既然是模型首先就是圍繞數據操做,上帝set,上帝get不能少,這樣也是爲了體現出對象封裝性
  2. 與此同時數據的清理與改變也是不能少的
  3. 監聽對象中屬性變化
  4. 爲對象添加驗證規則,以及錯誤提示
  5. 對象的獲取和保存,須要服務器端支持才能測試
  6. 等等一些圍繞的數據的處理了

 

 


繼承extend

就是把模板類的方法繼承給子類,因此咱們子類都具備相同的特性了

Backbone.Model.extend

Backbone.Collection.extend

Backbone.Router.extend

Backbone.View.extend

 

擴充的靜態方法

Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;

關於這個繼承很好理解,js的繼承經常使用的就是這個了

代碼簡單分析下

建立子類的載體,換句話就是咱們構造出來的那個新的類的一個新的構造器

if (protoProps && _.has(protoProps, 'constructor')) {
    child = protoProps.constructor;
} else {
    child = function () {
        return parent.apply(this, arguments);
    };
}

若是用戶自定義了constructor函數,就用這個,不然就內部自行構建

以後就是複製靜態屬性到新的child

而後把父類的原型鏈的引用給指向child

這個請參考http://www.cnblogs.com/aaronjs/archive/2012/08/26/2657103.html

image

擴展了屬性,在constructor中擴展了__super__ 指向父類,繼承了模板的原型鏈上的方法

 


上帝get/上帝set

能夠想像下模型實例用來存儲數據表中的一行數據(row)

Backbone利用model的attributes與數據庫的字段一一對應

使用set和get方法來設置或獲取模型的屬性。

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);
};

不能把屬性直接寫到 Backbone.Model.extend的擴展中,緣由也很簡單,一個是封裝性,最重要的原型上是共享的,若是是引用類型就糟糕了

因此屬性在模型實例上有一個專門的屬性來存儲:this.attributes,set/get都圍繞this.attributes操做。

 


監聽對象中屬性的變化(change)

  Models 用來建立數據,校驗數據,存儲數據到服務器端.Models 還能夠綁定事件。好比用戶動做變化觸發 models 的 change 事件,全部展現此model 數據的 views 都會接收到 這個 change 事件,進行重繪。

若是任何屬性的改變模型的狀態,「改變」事件將觸發模式

只是實現了一個自定義事件功能

監聽屬性color的改變

sidebar.on('change:color', function(model, color) {
    console.log('修改顏色',color)
});

設置改變

sidebar.set({color: 'white'});

 

源碼實現

Backbone.Model繼承了自定義事件Events

_.extend(Model.prototype, Events, {});

sidebar實例繼承了Backbone.Model.

var Sidebar = Backbone.Model.extend

因此Sidebar也具備自定義事件的功能,只是在set方法裏面按照規則觸發

使用 set() 方法建立或者設置屬性值能夠觸發自定義事件,

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);
    }
}

 

PS:

咱們知道雖然屬性是存儲this.attributes中,可是若是是直接

實例.attributes.name = "屬性名";

這樣很明顯就丟失了自定義事件了,因此使用 set() 是改變模型狀態並觸發其變動事件的惟一方法

Model 這一律念來對事件進行控制,可是這樣很好的使咱們將結構分離開,容易控制總體以及以後的變動都會變得異常簡單。

 


爲對象添加驗證規則,以及錯誤提示

驗證模型數據規範

var Chapter = Backbone.Model.extend({
  validate: function(attrs, options) {
    if (attrs.end < attrs.start) {
      return "can't end before it starts";
    }
  }
});

源碼部分

_validate: function (attrs, options) {
            if (!options.validate || !this.validate) return true;
            attrs = _.extend({}, this.attributes, attrs);
            var error = this.validationError = this.validate(attrs, options) || null;
            if (!error) return true;
            this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
            return false;
        }

執行了this.validate(attrs, options) 自定義驗證函數,可見若是返回了true

就會執行this.trigger('invalid', this, error, _.extend(options, {validationError: error})); 錯誤通知了

餘下的fetch,save,sync,url等等放在合集中在講吧

相關文章
相關標籤/搜索