BackBone模型源码
2013-06-06 11:12:01
Backbone 模型 - Backbone.Model
首先是 Model 模块的源码:
- // Backbone **Models** 在框架中是基础的数据对象 --
- // 常常代表你服务器中数据库表中的一行.
- // 为一个离散的数据块,和一堆对这些数据进行计算转换的有用的相关的方法
-
- // 使用指定的属性创建一个新的模型.
- // 会自动生成并分配一个用户id (`cid`),依赖与 Underscore 的几个方法
- var Model = Backbone.Model = function(attributes, options) {
- var defaults;
- var attrs = attributes || {};
- options || (options = {});
- // Underscore 中生成id的方法
- this.cid = _.uniqueId('c');
- this.attributes = {};
- // 返回 options 中属性存在于 modelOptions 的一个对象
- _.extend(this, _.pick(options, modelOptions));
- if (options.parse) attrs = this.parse(attrs, options) || {};
- if (defaults = _.result(this, 'defaults')) {
- attrs = _.defaults({}, attrs, defaults);
- }
- this.set(attrs, options);
- this.changed = {};
- this.initialize.apply(this, arguments);
- };
模型的构造函数,用于创建各个模型。各个模型都会拥有唯一的 cid ,
提供的
modelOptions 列表中的选项将直接连接到原型,其余 options 中的将使用 set 来处理,set是 Model 的核心操作。会触发模型"change"事件。具体属性的话可以在此了解, 拔了一张强大的图~
![](http://blog.chinaunix.net/attachment/201306/5/26672038_13704214713FfD.png)
以下为 Model 模块的原型中的方法,其中的 set 为核心方法,是控制当有属性改变时触发改变对象的 change 事件。 下面为 Model模块的使用以及原型方法的源码。
首先我们在页面上画一个黑色的矩形,并且 id 为 side。 我们使用模型控制进行颜色更改。
-
// 创建一个 有 changeColor 方法的模型,该方法会调用 set 更改 color 属性,set 会触发实例的 change:color 事件
-
var Sidebar = Backbone.Model.extend({
- changeColor:function(){
- var cssColor = prompt("输入颜色");
- this.set({color:cssColor});
- }
-
});
-
-
// 建立一个模型的实例
-
var side = new Sidebar();
-
-
// 为该实例绑定 当属性 color 被 set (也就是触发change)后的事件
- side.bind("change:color",function(model,color){
- $(".side").css({background:color});
- alert("setted");
-
});
-
-
//然后我们运行 模型实例中的方法
- side.changeColor();
虽然感觉过程有点绕,中间有了 Model 这一概念来对事件进行控制,但是这样很好的使我们将结构分离开,容易控制整体以及之后的变更都会变得异常简单。
接下来就是 Model 原型中的方法:
- _.extend(Model.prototype, Events, {
-
- // 当前与之前值有变化的 属性散列(哈希).
- changed: null,
-
- // 最后一次验证失败的返回值.
- validationError: null,
-
- // JSON 默认名称 `id` 属性名为 `"id"`. MongoDB 和
- // CouchDB 用户偏向于 `"_id"`.
- idAttribute: 'id',
-
- // 默认初始化空函数.
- // 用自己的逻辑初始化重写函数.
- initialize: function(){},
-
- // 返回一个模型的属性对象的拷贝.
- toJSON: function(options) {
- return _.clone(this.attributes);
- },
-
- // 默认使用 `Backbone.sync` 代理
- // 如果需要可以自定义从写
- sync: function() {
- return Backbone.sync.apply(this, arguments);
- },
-
- // 获取属性值
- get: function(attr) {
- return this.attributes[attr];
- },
-
- // 获取属性的 HTML-escaped 值.
- escape: function(attr) {
- return _.escape(this.get(attr));
- },
-
- // 若属性值不为 null 或者 undefined 则返回 `true`
- // or undefined.
- has: function(attr) {
- return this.get(attr) != null;
- },
-
- // 在对象上建立模型的属性哈希, 触发 `"change"`.
- // 这是 模型 的核心操作, 更新数据, 通知那些需要知道 模型 状态变化的对象
- // backbone 野兽的心脏.
- set: function(key, val, options) {
- var attr, attrs, unset, changes, silent, changing, prev, current;
- if (key == null) return this;
-
- // 处理 `"key", value` 和 `{key: value}` 2种参数形式的情况.
- if (typeof key === 'object') {
- attrs = key;
- options = val;
- } else {
- (attrs = {})[key] = val;
- }
-
- options || (options = {});
-
- // 执行验证.
- if (!this._validate(attrs, options)) return false;
-
- // 提取 属性 和 可选项.
- unset = options.unset;
- silent = options.silent;
- changes = [];
- changing = this._changing;
- this._changing = true;
-
- if (!changing) {
- this._previousAttributes = _.clone(this.attributes);
- this.changed = {};
- }
- current = this.attributes, prev = this._previousAttributes;
-
- // 检测 `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;
- } else {
- delete this.changed[attr];
- }
- unset ? delete current[attr] : current[attr] = val;
- }
-
- // 触发所用相应的属性改变.
- if (!silent) {
- if (changes.length) this._pending = true;
- for (var i = 0, l = changes.length; i < l; i++) {
- this.trigger('change:' + changes[i], this, current[changes[i]], options);
- }
- }
-
- // while 循环 修改将被递归嵌套在'events'事件中
- if (changing) return this;
- if (!silent) {
- while (this._pending) {
- this._pending = false;
- this.trigger('change', this, options);
- }
- }
- this._pending = false;
- this._changing = false;
- return this;
- },
-
- // 从模型中移除一个属性, 触发 `"change"`.
- // `unset` 如果属性不存在设置为空
- unset: function(attr, options) {
- return this.set(attr, void 0, _.extend({}, options, {unset: true}));
- },
-
- // 清楚模型中的所有属性,触发 `"change"`.
- clear: function(options) {
- var attrs = {};
- for (var key in this.attributes) attrs[key] = void 0;
- return this.set(attrs, _.extend({}, options, {unset: true}));
- },
-
- // 确保 模型 在上一次更改后再一次更改 `"change"` event.
- // 如果指定了属性名称, 确定属性已经改变.
- hasChanged: function(attr) {
- if (attr == null) return !_.isEmpty(this.changed);
- return _.has(this.changed, attr);
- },
-
- // 返回一个包含所有改变的属性的对象, 当没有属性被更改,就返回 false.
- // 常用来判断视图块是否需要更新或者那些属性需要保存到后端
- // 未设置的 属性将设置为 undefined.
- // 你也可以针对模型传递一个属性对象来改变,决定是否 *would be* 改变.
- changedAttributes: function(diff) {
- if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
- var val, changed = false;
- var old = this._changing ? this._previousAttributes : this.attributes;
- for (var attr in diff) {
- if (_.isEqual(old[attr], (val = diff[attr]))) continue;
- (changed || (changed = {}))[attr] = val;
- }
- return changed;
- },
-
- // 获取一个属性之前,在最后一次 'change' 事件触发时候的值
- previous: function(attr) {
- if (attr == null || !this._previousAttributes) return null;
- return this._previousAttributes[attr];
- },
-
- // 获取上一次 `"change"` 事件时所有属性的值.
- previousAttributes: function() {
- return _.clone(this._previousAttributes);
- },
-
- // 从服务器端获取 模型 .
- // 如果服务器端的显示的模型与当前属性有区别,那么覆盖并且触发事件"change"`.
- fetch: function(options) {
- options = options ? _.clone(options) : {};
- if (options.parse === void 0) options.parse = true;
- var model = this;
- var success = options.success;
- options.success = function(resp) {
- if (!model.set(model.parse(resp, options), options)) return false;
- if (success) success(model, resp, options);
- model.trigger('sync', model, resp, options);
- };
- wrapError(this, options);
- return this.sync('read', this, options);
- },
-
- // 设置属性的哈希, 同步模型到服务器.
- // 如果服务器返回一个有区别的属性散列,那么模型的状态要重新设置
- save: function(key, val, options) {
- var attrs, method, xhr, attributes = this.attributes;
-
- // 处理 `"key", value` and `{key: value}` 2种参数情况.
- if (key == null || typeof key === 'object') {
- attrs = key;
- options = val;
- } else {
- (attrs = {})[key] = val;
- }
-
- // 如果并不在等待队列中并且属性不存在, 保存行为以 `set(attr).save(null, opts)`格式.
- if (attrs && (!options || !options.wait) && !this.set(attrs, options)) return false;
-
- options = _.extend({validate: true}, options);
-
- // 舍弃无效的模型 .
- if (!this._validate(attrs, options)) return false;
-
- // 如果 `{wait: true}` 设置临时属性.
- if (attrs && options.wait) {
- this.attributes = _.extend({}, attributes, attrs);
- }
-
- // 在服务器端保存成功后, 客户端可以与服务器端一起更新(可选)
- if (options.parse === void 0) options.parse = true;
- var model = this;
- var success = options.success;
- options.success = function(resp) {
- // 确保属性在同步保存的时候可恢复.
- model.attributes = attributes;
- var serverAttrs = model.parse(resp, options);
- if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
- if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
- return false;
- }
- if (success) success(model, resp, options);
- model.trigger('sync', model, resp, options);
- };
- wrapError(this, options);
-
- method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
- if (method === 'patch') options.attrs = attrs;
- xhr = this.sync(method, this, options);
-
- // 恢复属性.
- if (attrs && options.wait) this.attributes = attributes;
-
- return xhr;
- },
-
- // 去除以及存在在服务器端的模型.
- // 如果集合中原有一个模型,那么去掉就好.
- // 如果 `wait: true` 传递过来, 在移除之前等待服务器响应.
- destroy: function(options) {
- options = options ? _.clone(options) : {};
- var model = this;
- var success = options.success;
-
- var destroy = function() {
- model.trigger('destroy', model, model.collection, options);
- };
-
- options.success = function(resp) {
- if (options.wait || model.isNew()) destroy();
- if (success) success(model, resp, options);
- if (!model.isNew()) model.trigger('sync', model, resp, options);
- };
-
- if (this.isNew()) {
- options.success();
- return false;
- }
- wrapError(this, options);
-
- var xhr = this.sync('delete', this, options);
- if (!options.wait) destroy();
- return xhr;
- },
-
- // 模型在服务器端表示的默认的URL -- if you're
- // 如果你在使用 Backbone's 静态方法, 重写此方法来改变将被调用的端点.
- url: function() {
- var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
- if (this.isNew()) return base;
- return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
- },
-
- // **parse** 响应转换成模型上的哈希
- // 默认只是实现通过响应.
- parse: function(resp, options) {
- return resp;
- },
-
- // 创建一个新的 和当前模型有一样属性的模型.
- clone: function() {
- return new this.constructor(this.attributes);
- },
-
- // 如果一个模型还没有存到服务器,那么他就是新的, 没有id.
- isNew: function() {
- return this.id == null;
- },
-
- // 检查模型当前是不是有效状态.
- isValid: function(options) {
- return this._validate({}, _.extend(options || {}, { validate: true }));
- },
-
- // 对下一个完全设置了模型属性的进行验证
- // 返回'true'. 否则触发 `"invalid"` 事件.
- _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;
- }
-
- });
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)