前端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 應用程序的核心,包括交互數據及相關的大量邏輯: 轉換、驗證、計算屬性和訪問控制。

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

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

 


如何設計模型

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

用別人的框架,就需要了解別人的規則,這種學習成本是跑不掉的 - -

官方的demo

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

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模型類

所以具有了on ,set 等等這種基礎的屬性與方法

 


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等等放在合集中在講吧


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM