Backbone源码学习 — Backbone.Model

发表于 Backbone 分类,标签:

Backbone源码学习 — Backbone.Model

今天我们来谈谈Backbone.js MVC 中的 M , Model是backbone的核心部分,包含着页面展示内容的数据,还有围绕着数据操作的各种 转换,校验,计算 ,权限控制,服务端交互等等操作,你可以通过 Backbone.Model.extend() 生成你的model , 当然生成的model也可以作为一个基类去向下扩展更多的model

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

Backbone.Model Api

Backbone.Model 提供了大量方法用于实现一个Model的基本操作,当然其中最基本的还是基于 Backbone.Events 的事件机制,在Model的attributes发生变化的时候,相应的 change:attr事件会被触发,下面是提供的API:

其中有对数据进行服务端操作的方法:

  • sync : 包装了Backbone.syncxhr的基类

  • fetch : 用于从服务端获取数据

  • save : 向服务端持久化数据

  • destroy: 从服务端删除数据

model中数据操作的方法:

  • get : 从attributes中获取数据

  • set : 向attributes中设置数据

  • escape : 对数据进行编码 ,使用的是underscore的 _.escape

  • has : attributes中有无对应数据

  • unset : 从attributes中删除数据

  • clear : 清空attributes数据

  • changed : 与上个状态(执行过setunset),相比变化的值

  • 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进行讲解:

构造函数

12345678910111213
       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中的parsetrue,那么会对初始化数值通过parse方法进行一个解析,调用set方法,所有的初始值会被存入attributes中,调用initialize初始化方法 。model的初始化就完成了。

set

model.set(attributes, [options])

set方法会将值设置进入attribute中,设置时如果设置了slienttrue,会触发相应的 change:attr 的事件,最后统一触发change事件,set方法部分代码如下:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
    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:attrchange事件。

save

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

save方法用于向客户端持久化数据,会根据数据的不同和配置的不同选择使用create,update或者是patch,并且触发 sync 事件,以下为部分代码:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
    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() ? '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对象        return xhr;    }

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

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


https://cdnjs.com/libraries/backbone.js/tutorials/what-is-a-model/ (英文)


转自(http://qbright.github.io/blog/2014/12/04/backbone.Model/  )

0 篇评论

发表我的评论