Chinaunix首页 | 论坛 | 博客
  • 博客访问: 812113
  • 博文数量: 62
  • 博客积分: 526
  • 博客等级: 二等列兵
  • 技术积分: 2078
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-04 20:41
个人简介

博客迁移至 freefe.cc

文章分类

全部博文(62)

分类: JavaScript

2013-06-06 11:12:01

Backbone 模型 - Backbone.Model


首先是 Model 模块的源码:

  1.   // Backbone **Models** 在框架中是基础的数据对象 --
  2.   // 常常代表你服务器中数据库表中的一行.
  3.   // 为一个离散的数据块,和一堆对这些数据进行计算转换的有用的相关的方法

  4.   // 使用指定的属性创建一个新的模型.
  5.   // 会自动生成并分配一个用户id (`cid`),依赖与 Underscore 的几个方法
  6.   var Model = Backbone.Model = function(attributes, options) {
  7.     var defaults;
  8.     var attrs = attributes || {};
  9.     options || (options = {});

  10.    // Underscore 中生成id的方法
  11.     this.cid = _.uniqueId('c');
  12.     this.attributes = {};

  13.     // 返回 options 中属性存在于 modelOptions 的一个对象
  14.     _.extend(this, _.pick(options, modelOptions));
  15.     if (options.parse) attrs = this.parse(attrs, options) || {};
  16.     if (defaults = _.result(this, 'defaults')) {
  17.       attrs = _.defaults({}, attrs, defaults);
  18.     }
  19.     this.set(attrs, options);
  20.     this.changed = {};
  21.     this.initialize.apply(this, arguments);
  22.   };

模型的构造函数,用于创建各个模型。各个模型都会拥有唯一的 cid ,提供的  modelOptions  列表中的选项将直接连接到原型,其余 options 中的将使用 set 来处理,set是 Model 的核心操作。会触发模型"change"事件。具体属性的话可以在此了解, 拔了一张强大的图~





以下为 Model 模块的原型中的方法,其中的 set 为核心方法,是控制当有属性改变时触发改变对象的 change 事件。 下面为 Model模块的使用以及原型方法的源码。

首先我们在页面上画一个黑色的矩形,并且 id 为 side。 我们使用模型控制进行颜色更改。

  1. // 创建一个 有 changeColor 方法的模型,该方法会调用 set 更改 color 属性,set 会触发实例的 change:color 事件
  2. var Sidebar = Backbone.Model.extend({
  3.     changeColor:function(){
  4.         var cssColor = prompt("输入颜色");
  5.         this.set({color:cssColor});
  6.     }
  7. });

  8. // 建立一个模型的实例
  9. var side = new Sidebar();

  10. // 为该实例绑定 当属性 color 被 set (也就是触发change)后的事件
  11. side.bind("change:color",function(model,color){
  12.     $(".side").css({background:color});
  13.     alert("setted");
  14. });

  15. //然后我们运行 模型实例中的方法
  16. side.changeColor();
虽然感觉过程有点绕,中间有了 Model 这一概念来对事件进行控制,但是这样很好的使我们将结构分离开,容易控制整体以及之后的变更都会变得异常简单。

接下来就是 Model 原型中的方法:


点击(此处)折叠或打开

  1. _.extend(Model.prototype, Events, {

  2.     // 当前与之前值有变化的 属性散列(哈希).
  3.     changed: null,

  4.     // 最后一次验证失败的返回值.
  5.     validationError: null,

  6.     // JSON 默认名称 `id` 属性名为 `"id"`. MongoDB 和
  7.     // CouchDB 用户偏向于 `"_id"`.
  8.     idAttribute: 'id',

  9.     // 默认初始化空函数.
  10.     // 用自己的逻辑初始化重写函数.
  11.     initialize: function(){},

  12.     // 返回一个模型的属性对象的拷贝.
  13.     toJSON: function(options) {
  14.       return _.clone(this.attributes);
  15.     },

  16.     // 默认使用 `Backbone.sync` 代理
  17.     // 如果需要可以自定义从写
  18.     sync: function() {
  19.       return Backbone.sync.apply(this, arguments);
  20.     },

  21.     // 获取属性值
  22.     get: function(attr) {
  23.       return this.attributes[attr];
  24.     },

  25.     // 获取属性的 HTML-escaped 值.
  26.     escape: function(attr) {
  27.       return _.escape(this.get(attr));
  28.     },

  29.     // 若属性值不为 null 或者 undefined 则返回 `true`
  30.     // or undefined.
  31.     has: function(attr) {
  32.       return this.get(attr) != null;
  33.     },

  34.     // 在对象上建立模型的属性哈希, 触发 `"change"`.
  35.     // 这是 模型 的核心操作, 更新数据, 通知那些需要知道 模型 状态变化的对象
  36.     // backbone 野兽的心脏.
  37.     set: function(key, val, options) {
  38.       var attr, attrs, unset, changes, silent, changing, prev, current;
  39.       if (key == null) return this;

  40.       // 处理 `"key", value` 和 `{key: value}` 2种参数形式的情况.
  41.       if (typeof key === 'object') {
  42.         attrs = key;
  43.         options = val;
  44.       } else {
  45.         (attrs = {})[key] = val;
  46.       }

  47.       options || (options = {});

  48.       // 执行验证.
  49.       if (!this._validate(attrs, options)) return false;

  50.       // 提取 属性 和 可选项.
  51.       unset = options.unset;
  52.       silent = options.silent;
  53.       changes = [];
  54.       changing = this._changing;
  55.       this._changing = true;

  56.       if (!changing) {
  57.         this._previousAttributes = _.clone(this.attributes);
  58.         this.changed = {};
  59.       }
  60.       current = this.attributes, prev = this._previousAttributes;

  61.       // 检测 `id` 变化.
  62.       if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];

  63.       // 设置每一个属性, 更新或者删除当前值.
  64.       for (attr in attrs) {
  65.         val = attrs[attr];
  66.         if (!_.isEqual(current[attr], val)) changes.push(attr);
  67.         if (!_.isEqual(prev[attr], val)) {
  68.           this.changed[attr] = val;
  69.         } else {
  70.           delete this.changed[attr];
  71.         }
  72.         unset ? delete current[attr] : current[attr] = val;
  73.       }

  74.       // 触发所用相应的属性改变.
  75.       if (!silent) {
  76.         if (changes.length) this._pending = true;
  77.         for (var i = 0, l = changes.length; i < l; i++) {
  78.           this.trigger('change:' + changes[i], this, current[changes[i]], options);
  79.         }
  80.       }

  81.       // while 循环 修改将被递归嵌套在'events'事件中
  82.       if (changing) return this;
  83.       if (!silent) {
  84.         while (this._pending) {
  85.           this._pending = false;
  86.           this.trigger('change', this, options);
  87.         }
  88.       }
  89.       this._pending = false;
  90.       this._changing = false;
  91.       return this;
  92.     },

  93.     // 从模型中移除一个属性, 触发 `"change"`.
  94.     // `unset` 如果属性不存在设置为空
  95.     unset: function(attr, options) {
  96.       return this.set(attr, void 0, _.extend({}, options, {unset: true}));
  97.     },

  98.     // 清楚模型中的所有属性,触发 `"change"`.
  99.     clear: function(options) {
  100.       var attrs = {};
  101.       for (var key in this.attributes) attrs[key] = void 0;
  102.       return this.set(attrs, _.extend({}, options, {unset: true}));
  103.     },

  104.     // 确保 模型 在上一次更改后再一次更改 `"change"` event.
  105.     // 如果指定了属性名称, 确定属性已经改变.
  106.     hasChanged: function(attr) {
  107.       if (attr == null) return !_.isEmpty(this.changed);
  108.       return _.has(this.changed, attr);
  109.     },

  110.     // 返回一个包含所有改变的属性的对象, 当没有属性被更改,就返回 false.
  111.     // 常用来判断视图块是否需要更新或者那些属性需要保存到后端
  112.     // 未设置的 属性将设置为 undefined.
  113.     // 你也可以针对模型传递一个属性对象来改变,决定是否 *would be* 改变.
  114.     changedAttributes: function(diff) {
  115.       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
  116.       var val, changed = false;
  117.       var old = this._changing ? this._previousAttributes : this.attributes;
  118.       for (var attr in diff) {
  119.         if (_.isEqual(old[attr], (val = diff[attr]))) continue;
  120.         (changed || (changed = {}))[attr] = val;
  121.       }
  122.       return changed;
  123.     },

  124.     // 获取一个属性之前,在最后一次 'change' 事件触发时候的值
  125.     previous: function(attr) {
  126.       if (attr == null || !this._previousAttributes) return null;
  127.       return this._previousAttributes[attr];
  128.     },

  129.     // 获取上一次 `"change"` 事件时所有属性的值.
  130.     previousAttributes: function() {
  131.       return _.clone(this._previousAttributes);
  132.     },

  133.     // 从服务器端获取 模型 .
  134.     // 如果服务器端的显示的模型与当前属性有区别,那么覆盖并且触发事件"change"`.
  135.     fetch: function(options) {
  136.       options = options ? _.clone(options) : {};
  137.       if (options.parse === void 0) options.parse = true;
  138.       var model = this;
  139.       var success = options.success;
  140.       options.success = function(resp) {
  141.         if (!model.set(model.parse(resp, options), options)) return false;
  142.         if (success) success(model, resp, options);
  143.         model.trigger('sync', model, resp, options);
  144.       };
  145.       wrapError(this, options);
  146.       return this.sync('read', this, options);
  147.     },

  148.     // 设置属性的哈希, 同步模型到服务器.
  149.     // 如果服务器返回一个有区别的属性散列,那么模型的状态要重新设置
  150.     save: function(key, val, options) {
  151.       var attrs, method, xhr, attributes = this.attributes;

  152.       // 处理 `"key", value` and `{key: value}` 2种参数情况.
  153.       if (key == null || typeof key === 'object') {
  154.         attrs = key;
  155.         options = val;
  156.       } else {
  157.         (attrs = {})[key] = val;
  158.       }

  159.       // 如果并不在等待队列中并且属性不存在, 保存行为以 `set(attr).save(null, opts)`格式.
  160.       if (attrs && (!options || !options.wait) && !this.set(attrs, options)) return false;

  161.       options = _.extend({validate: true}, options);

  162.       // 舍弃无效的模型 .
  163.       if (!this._validate(attrs, options)) return false;

  164.       // 如果 `{wait: true}` 设置临时属性.
  165.       if (attrs && options.wait) {
  166.         this.attributes = _.extend({}, attributes, attrs);
  167.       }

  168.       // 在服务器端保存成功后, 客户端可以与服务器端一起更新(可选)
  169.       if (options.parse === void 0) options.parse = true;
  170.       var model = this;
  171.       var success = options.success;
  172.       options.success = function(resp) {
  173.         // 确保属性在同步保存的时候可恢复.
  174.         model.attributes = attributes;
  175.         var serverAttrs = model.parse(resp, options);
  176.         if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
  177.         if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
  178.           return false;
  179.         }
  180.         if (success) success(model, resp, options);
  181.         model.trigger('sync', model, resp, options);
  182.       };
  183.       wrapError(this, options);

  184.       method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
  185.       if (method === 'patch') options.attrs = attrs;
  186.       xhr = this.sync(method, this, options);

  187.       // 恢复属性.
  188.       if (attrs && options.wait) this.attributes = attributes;

  189.       return xhr;
  190.     },

  191.     // 去除以及存在在服务器端的模型.
  192.     // 如果集合中原有一个模型,那么去掉就好.
  193.     // 如果 `wait: true` 传递过来, 在移除之前等待服务器响应.
  194.     destroy: function(options) {
  195.       options = options ? _.clone(options) : {};
  196.       var model = this;
  197.       var success = options.success;

  198.       var destroy = function() {
  199.         model.trigger('destroy', model, model.collection, options);
  200.       };

  201.       options.success = function(resp) {
  202.         if (options.wait || model.isNew()) destroy();
  203.         if (success) success(model, resp, options);
  204.         if (!model.isNew()) model.trigger('sync', model, resp, options);
  205.       };

  206.       if (this.isNew()) {
  207.         options.success();
  208.         return false;
  209.       }
  210.       wrapError(this, options);

  211.       var xhr = this.sync('delete', this, options);
  212.       if (!options.wait) destroy();
  213.       return xhr;
  214.     },

  215.     // 模型在服务器端表示的默认的URL -- if you're
  216.     // 如果你在使用 Backbone's 静态方法, 重写此方法来改变将被调用的端点.
  217.     url: function() {
  218.       var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
  219.       if (this.isNew()) return base;
  220.       return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
  221.     },

  222.     // **parse** 响应转换成模型上的哈希
  223.     // 默认只是实现通过响应.
  224.     parse: function(resp, options) {
  225.       return resp;
  226.     },

  227.     // 创建一个新的 和当前模型有一样属性的模型.
  228.     clone: function() {
  229.       return new this.constructor(this.attributes);
  230.     },

  231.     // 如果一个模型还没有存到服务器,那么他就是新的, 没有id.
  232.     isNew: function() {
  233.       return this.id == null;
  234.     },

  235.     // 检查模型当前是不是有效状态.
  236.     isValid: function(options) {
  237.       return this._validate({}, _.extend(options || {}, { validate: true }));
  238.     },

  239.     // 对下一个完全设置了模型属性的进行验证
  240.     // 返回'true'. 否则触发 `"invalid"` 事件.
  241.     _validate: function(attrs, options) {
  242.       if (!options.validate || !this.validate) return true;
  243.       attrs = _.extend({}, this.attributes, attrs);
  244.       var error = this.validationError = this.validate(attrs, options) || null;
  245.       if (!error) return true;
  246.       this.trigger('invalid', this, error, _.extend(options || {}, {validationError: error}));
  247.       return false;
  248.     }

  249.   });


阅读(2856) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~