首頁 > web前端 > js教程 > 主體

簡單了解Backbone.js的Model模型以及View視圖的來源碼_基礎知識

WBOY
發布: 2016-05-16 15:15:48
原創
1032 人瀏覽過

Backbone.Model

今天我們先來談談Backbone.js MVC 中的M , Model是backbone的核心部分,包含著頁面展示內容的數據,還有圍繞著數據操作的各種轉換,校驗,計算,權限控制,服務端互動等等操作,你可以透過Backbone.Model.extend() 產生你的model , 當然生成的model也可以作為一個基類去向下擴展更多的model

 var People = Backbone.Model.extend({
     
 });

 var Man = People.extend({

 });

登入後複製

Backbone.Model Api

Backbone.Model 提供了大量方法用於實現一個Model的基本操作,當然其中最基本的還是基於Backbone.Events 的事件機制,在Model的attributes發生變化的時候,相應的change:attr事件會被觸發,以下是提供的API:

2016214171632823.png (207×574)

其中有對資料進行服務端操作的方法:

  • sync : 包裝了Backbone.sync,xhr的基底類別
  • fetch : 用於從服務端取得資料
  • save : 向服務端持久化資料
  • destroy: 從服務端刪除資料

model中資料操作的方法:

  • get : 從attributes取得資料
  • set : 在attributes中設定資料
  • escape : 對資料進行編碼 ,使用的是underscore的 _.escape
  • has : attributes中有無對應資料
  • unset : 從attributes中刪除資料
  • clear : 清空attributes資料
  • changed : 與上個狀態(執行過set,unset),相比變化的值
  • toJSON : 將 attributes 序列化成一個物件
  • parse : 當設定項parse為真的時候,初始化/set/unset/fetch等資料操作中會對目標資料進行一個解析返回解析後的對象,此方法為空方法,需要重寫覆蓋
  • hasChanged : 與上個狀態(執行過set,unset),相比是否發生過變化
  • changeAttributes : 與上個狀態(執行過set,unset),相比所發生的所有值
  • previous : 前一狀態 (執行過set,unset),該屬性對應的值
  • previousAttributes : 與上個狀態(執行過set,unset),發生過變化對象的前一個狀態的所有值

model中資料校驗的方法:

  • validate:用於對model中資料進行校驗,需要重寫覆蓋預設方法
  • validationError : 傳回最近一個invalid時回傳的值
  • isValid : 呼叫_validate方法

下面會針對一些重點的api來講解:

建構子

  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);
   };
登入後複製

建構函式主要對初始化的資料和選項進行設置,然後會對產生一個唯一的cid用於標示model,如果options中的parse為true,那麼會對初始化數值透過parse方法進行一個解析,呼叫set方法,所有的初始值會被存入attributes中,呼叫initialize初始化方法。 model的初始化就完成了。

set

model.set(attributes, [options])

登入後複製

set方法會將值設定進入attribute中,設定時如果設定了slient為true,會觸發對應的 change:attr 的事件,最後統一觸發change事件,set方法部分程式碼如下:

 

  set: function(key, val, options) {
     //......

      // key值可以是键值对,也可以是一个字符串,将赋值传入attrs属性中  
    if (typeof key === 'object') {
     attrs = key;
     options = val;
    } else {
     (attrs = {})[key] = val;
    }

   // ....

    //对设置的值进行校验 
      if (!this._validate(attrs, options)) return false;
    unset = options.unset; // unset为true时会删除设置的值,unset方法就是通过 set(key,val,{unset:true})去实现的
   
    //当对象正在被被设置的时候,不给 previousAttributes 赋值
    if (!changing) {
     this._previousAttributes = _.clone(this.attributes);
     this.changed = {};
    }
 
    current = this.attributes, prev = this._previousAttributes;
 
    //如果对Id进行了设置,则对对象的id属性也进行改变
    if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
 

    //进行 设置或者是删除操作
    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进行设置
     } else {
      delete this.changed[attr];
     }
     unset ? delete current[attr] : current[attr] = val;//如果unset被设置成true了,则进行删除操作
    }
 
    //在silent不为false 的情况下,进行change:attr事件发送
    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);
     }
    }
 

    //触发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的整個流程就是對傳入的數值進行處理,變成一個鍵值對,然後對數值進行校驗,檢查正確性,然後開始進行設定操作,設定時檢查數值時候是改變的,如果有則加入changeed的物件中,然後檢查unset的值,進行對應的新增更新刪除操作。然後依序觸發change:attr和change事件。

save

model.save([attributes], [options])

登入後複製

save方法用於向客戶端持久化數據,會根據數據的不同和配置的不同選擇使用create,update或者是patch,並且觸發 sync 事件,以下為部分代碼:

  save: function(key, val, options) {     
    // ......
 

 
    //当设置了wait属性true的时候 , save方法先不执行set方法(不触发change事件),只执行validate
    if (attrs && !options.wait) {
     if (!this.set(attrs, options)) return false;
    } else {
     if (!this._validate(attrs, options)) return false;
    }

    //如果wait为true,设置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 , 那么会在请求返回之后才进行set操作
     if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
     if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
      return false;
     }
     if (success) success(model, resp, options);
     //触发 sync 事件
     model.trigger('sync', model, resp, options);
    };

    //生成XHR onerror 回调函数
    wrapError(this, options);
    //选择方法
    method = this.isNew() &#63; 'create' : (options.patch &#63; 'patch' : 'update');
    if (method === 'patch') options.attrs = attrs;
    xhr = this.sync(method, this, options);
 
    // Restore attributes.
    if (attrs && options.wait) this.attributes = attributes;
    //返回xhr对象
    return xhr;
  }

登入後複製


save 中最需要注意的就是 wait 的设置,当wait为真的时候,save返回会在xhr返回之后再执行set操作,而不是在xhr之前就进行set操作,因此change事件的触发时机也就不同了。

之前说过整个Backbone都是通过事件串联起来的,所以对于事件触发时机的了解和把握是非常重要的,不然会在开发过程中导致一些奇怪的问题出现。

Backbone.View
前面已经对backbone中的Event、Model、Collection代码进行了分析,现在我们来看下MVC中的V部分,也就是Backbone.View,View在Backbone中主要用于沟通页面中的DOM和Backbone.Model/Collection,页面的逻辑操作,DOM事件的绑定等,View部分的代码非常简答,加上注释只有110左右。 View部分有一下API:

2016214171843674.png (197×245)

方法不多,下面对部分API进行介绍:

构造方法

  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();
 };
登入後複製

构造方法中为View生成了一个唯一的cid,以'view'开头,然后进行对目标属性viewOptions进行合并,接着调用_ensureElement判断el的情况,接着调用delegateEvents进行方法绑定,初始化完成 。

delegateEvents

view.setElement(element)

   setElement: function(element, delegate) {
    if (this.$el) this.undelegateEvents();//如果已经存在this.$el,进行事件解绑

    //对$el进行赋值,本质是一个jquery或者是 Lo-Dash and Zepto 对象
    this.$el = element instanceof Backbone.$ &#63; element : Backbone.$(element);
    //把dom element 赋值给el
    this.el = this.$el[0];

     //如果没有显式传值,则进行事件绑定
    if (delegate !== false) this.delegateEvents();
    return this;
   }

登入後複製

setElement方法用于设置View对应的element , 这个方法在new的时候会被调用, 如果想要在使用过程中改变View的dom元素指向,可调用这个方法进行重新设置

_ensureElement

  _ensureElement: function() {
     //如果已经对el进行设置,直接调用setElement方法
    if (!this.el) {//如果没有设置,生成一个元素对象,再调用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这个方法是内部方法,在构造函数中使用,用于判断指定的el在页面中存不存在,如果存在则对$el进行赋值,如果不存在,则生成一个$el,但是要注意这个对象是没有落地到dom树中的 。

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();
   
      //处理每个事件
      for (var key in events) {
       var method = events[key];
       //解析回调函数
       if (!_.isFunction(method)) method = this[events[key]];
       if (!method) continue;
   
       //对选择器进行分析
       var match = key.match(delegateEventSplitter);
       var eventName = match[1], selector = match[2];
       method = _.bind(method, this);
   
       //绑定的事件名都是以 eventName + '.delegateEvents' + cid 组成,
       //这么做能够在undelegateEvents的时候选择到这个View的所有事件
       eventName += '.delegateEvents' + this.cid;
       if (selector === '') {
        this.$el.on(eventName, method);
       } else {
        this.$el.on(eventName, selector, method);
       }
      }
      return this;
   }

登入後複製

在View中你可以使用一个 key:value 集合指定对应的事件,在初始化的时候构造函数会调用delegateEvents进行绑定,需要注意的是所有在key中指定的元素的父元素都必须是$el,也就是说元素必须是$el的子节点,否则绑定失败。

View和其他backbone模块一个区别就是没有自己的内建自定义事件,当然他也组合了Events模块,但是所有的事件都需要自己进行建立。View主要是一个MVC模型的承载,其实真正的功能不多,其实从模型层面上看V在前端开发中是最接近业务逻辑的,所以在View中大部分的逻辑都是开发者自己去扩展的。

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板