backbone模型层浅析
Model层有两个类: Model, Collection
1.Model
不翻文档,我们用代码说话。
首先分析下类。
var myM = Backbone.Model.extend({})//构造一个Model类,myM
这个类居然是空的,没有官方所说的那么多属性啊,难道underscore失灵了?
>_.keys(myM)
["extend", "__super__"]
>_.keys(Backbone.Model)
["extend"]
默认情况下,子类比父类多了一个__super__的东东,这到底是啥?
>_.isObject(myM.__super__)
true
对象。 在观察下:
>_.keys(myM.__super__)
["on", "once", "off", "trigger", "stopListening", "listenTo", "listenToOnce", "bind", "unbind", "changed", "validationError", "idAttribute", "initialize", "toJSON", "sync", "get", "escape", "has", "set", "unset", "clear", "hasChanged", "changedAttributes", "previous", "previousAttributes", "fetch", "save", "destroy", "url", "parse", "clone", "isNew", "isValid", "_validate", "keys", "values", "pairs", "invert", "pick", "omit"]
结论:父类的指针? 官方文档中列举的接口(函数/事件/属性)出现了,如上。
比较关心数据的同步,看看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);
}
可以看出,可以有success回调,myM.fetch({success:function(model,response,options){}); 即成功的回调函数接受三个参数。
其次是fetch会调用本身的sync函数(最后一行)。
function () {
return Backbone.sync.apply(this, arguments);
}
云里雾里。过。
---------------分割线-------------------------
分析类的实例。
构造一个myM类的实例并分析:
>m_obj= new myM;
s {cid: "c6", attributes: Object, _changing: false, _previousAttributes: Object, changed: Object…}
>_.isObject(m_obj)
true
>_.keys(m_obj)
["cid", "attributes", "_changing", "_previousAttributes", "changed", "_pending"]
首先可以看出,实例的属性,跟类的属性很不相同,且跟父类的属性似乎一点关系都没有(没有继承?)。
真的是这样么?
>_.functions(m_obj)
["_validate", "bind", "changedAttributes", "clear", "clone", "constructor", "destroy", "escape", "fetch", "get", "has", "hasChanged", "initialize", "invert", "isNew", "isValid", "keys", "listenTo", "listenToOnce", "off", "omit", "on", "once", "pairs", "parse", "pick", "previous", "previousAttributes", "save", "set", "stopListening", "sync", "toJSON", "trigger", "unbind", "unset", "url", "values"]
好吧,这个跟myM.__super__有一定的交集。交集为:
>m_obj_funcs = _.functions(m_obj)
base_funcs = _.functions(myM.__super__)
_.filter(base_funcs, function(f){return _.contains(m_obj_funcs,f)}) ["_validate", "bind", "changedAttributes", "clear", "clone", "destroy", "escape", "fetch", "get", "has", "hasChanged", "initialize", "invert", "isNew", "isValid", "keys", "listenTo", "listenToOnce", "off", "omit", "on", "once", "pairs", "parse", "pick", "previous", "previousAttributes", "save", "set", "stopListening", "sync", "toJSON", "trigger", "unbind", "unset", "url", "values"]
m_obj又有多出来哪些函数呢?
>_.filter(m_obj_funcs, function(f){return !_.contains(base_funcs,f);})
["constructor"]
似乎是构造器
>m_obj.constructor
function (){ return parent.apply(this, arguments); }
属性
["cid", "attributes", "_changing", "_previousAttributes", "changed", "_pending"]
(提示:回到之前构造m_obj的部分看看。)
先剧透下,attributes是属性键值对集合(JSON对象),可写(如果只要一个只读的attributes,使用对象实例的toJSON()方法)。比如,
>m_obj.set({'name':'tommy','blog':'http://www.cnblogs.com/Tommy-Yu'})
>m_obj.attributes
Object {name: "tommy", blog: "http://www.cnblogs.com/Tommy-Yu"}
其次,changed也是一个JSON对象,记录该实例哪些属性被改变了---只记录修改的部分,会被backbone分析,然后传给change事件的处理方法。如下:
>m_obj.set({'name':'joe'})
>m_obj.changed
Object {name: "joe"}
_previousAttributes,记录变更前的对象,有利于数据进行对比,和还原(比如撤销操作)。
>m_obj._previousAttributes
Object {name: "tommy", blog: "http://www.cnblogs.com/Tommy-Yu"}
至于cid这东东,一个临时的id吧。自动给某个类(比如myM)的对象按照数字顺序编号。
最后就是changing。
函数
["_validate", "bind", "changedAttributes", "clear", "clone", "destroy", "escape", "fetch", "get", "has", "hasChanged", "initialize", "invert", "isNew", "isValid", "keys", "listenTo", "listenToOnce", "off", "omit", "on", "once", "pairs", "parse", "pick", "previous", "previousAttributes", "save", "set", "stopListening", "sync", "toJSON", "trigger", "unbind", "unset", "url", "values"]
鉴于篇幅有限,介绍重点的:
initialize()--- 在对象被new出来的时候调用,可以在这里做一些初始化。
--------------小分割------------
set() ---上面已经演示过了,设置对象的attributes属性。set之前会调用_validate(attributes)方法--如果有定义---返回值为验证是否通过(true/false)。
has(attr) -- 判断对象的attributes是否具有attr属性
>m_obj.has('name')
true
>m_obj.has('age')
false
get(attr) -- 获取对象的attributes属性中的某一个key的值:
>m_obj.get('name')
"tommy"
-------------小分割-------------
previousAttributes() --- _previousAttributes属性的get封装
>m_obj.previousAttributes()
Object {name: "tommy", blog: "http://www.cnblogs.com/Tommy-Yu"}
previous(key) -- 等同于 _previousAttributes[key]
>m_obj.previous('name')
"tommy"
-------------小分割-------------
toJSON() -- 转化为json对象
>m_obj.toJSON()
Object {name: "tommy", blog: "http://www.cnblogs.com/Tommy-Yu"}
keys() -- 将键转化为数组
>m_obj.keys()
["name", "blog"]
values() -- 将值转化为数组
m_obj.values()
["tommy", "http://www.cnblogs.com/Tommy-Yu"]
-------------小分割-------------
isNew() -- 是否客户端新建对象(与服务器同步时用到)
>m_obj.isNew()
true
isValide()-- 对象是否通过了验证
会调用_validate(attributes)。attributes是键值对集合。
-------------小分割------------
bind(时间代码, 时间处理函数)
事件代码有:change, error, sync, ....
分别对应不同的处理函数,可以为匿名函数。
-------------小分割-------------
CRUD操作: fetch/save/destroy
比较一般的:sync
前提:配置url(string/funciton --> ajax路径)
fetch/save/destroy/sync 委托到Backbone.sync函数,此函数为Backbone与服务器通讯的核心,Backbone.sync代码如下:
function (method, model, options) {
var type = methodMap[method]; // Default options, unless specified.
_.defaults(options || (options = {}), {
emulateHTTP: Backbone.emulateHTTP,
emulateJSON: Backbone.emulateJSON
}); // Default JSON-request options.
var params = {type: type, dataType: 'json'}; // Ensure that we have a URL.
if (!options.url) {
params.url = _.result(model, 'url') || urlError();
} // Ensure that we have the appropriate request data.
if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
params.contentType = 'application/json';
params.data = JSON.stringify(options.attrs || model.toJSON(options));
} // For older servers, emulate JSON by encoding the request into an HTML-form.
if (options.emulateJSON) {
params.contentType = 'application/x-www-form-urlencoded';
params.data = params.data ? {model: params.data} : {};
} // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
// And an `X-HTTP-Method-Override` header.
if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
params.type = 'POST';
if (options.emulateJSON) params.data._method = type;
var beforeSend = options.beforeSend;
options.beforeSend = function(xhr) {
xhr.setRequestHeader('X-HTTP-Method-Override', type);
if (beforeSend) return beforeSend.apply(this, arguments);
};
} // Don't process data on a non-GET request.
if (params.type !== 'GET' && !options.emulateJSON) {
params.processData = false;
} // If we're sending a `PATCH` request, and we're in an old Internet Explorer
// that still has ActiveX enabled by default, override jQuery to use that
// for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
if (params.type === 'PATCH' && noXhrPatch) {
params.xhr = function() {
return new ActiveXObject("Microsoft.XMLHTTP");
};
} // Make the request, allowing the user to override any Ajax options.
var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
model.trigger('request', model, xhr, options);
return xhr;
}
其主要是调用Backbone.ajax来实现数据的交互,并返回jqXHR( jquery XmlHttpResponse)。
挑选一个,比如save来看看是如何调用Backbone.sync的,save代码如下:
function (key, val, options) {
var attrs, method, xhr, attributes = this.attributes; // Handle both `"key", value` and `{key: value}` -style arguments.
if (key == null || typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;//attrs => Object {key: val}
} options = _.extend({validate: true}, options); // If we're not waiting and attributes exist, save acts as
// `set(attr).save(null, opts)` with validation. Otherwise, check if
// the model will be valid when the attributes, if any, are set.
if (attrs && !options.wait) {
if (!this.set(attrs, options)) return false;
} else {
if (!this._validate(attrs, options)) return false;
} // Set temporary attributes if `{wait: true}`.
if (attrs && options.wait) {
this.attributes = _.extend({}, attributes, attrs);
} // After a successful server-side save, the client is (optionally)
// updated with the server-side state.
if (options.parse === void 0) options.parse = true;
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);
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); // Restore attributes.
if (attrs && options.wait) this.attributes = attributes; return xhr;
}
注意到书第四句:
xhr = this.sync(method, this, options);
就这样,save将工作丢给了sync,其主要工作是加工options和method和设置回调以及丢给sync之前的数据验证。
以上抓到几个有用的信息点:
1. 可以在options里面传入url,它的优先级比Model里面的url要高。
2. 可以重写Model的sync,来观察model到底对这些输入参数做了什么。
3. 可以在options里面设置success回调。dataType默认是json。
4. options里面的其他选项:
- url , 优先级比Backbone.Model.url要高
- data , 如果没设置,则 contenttype有可能为'application/json'
- attrs , data的候补
- emulateJSON , 兼容旧的server,JSON-> HTML Form , ContentType为'application/x-www-form-urlencoded', data为空
- emulateHTTP , 此选项用于兼容更旧的server, 在http头里面加入 X-HTTP-Method-Override等
- wait , 是否调用_validate函数
- parse, 函数/void 0, 客户端解析server传回的数据的函数,默认实现为:
function (resp, options) {
return resp;
} - success, 成功回调 function(model, reponse, options)
- patch ,??
fetch的调用链
fetch -> this.sync -> Backbone.sync -> success(model, reponse, options) -> this.parse -> this.set -> this._validate -> changed -> update view
由于篇幅关系,代码就不在这里上了。简单来说,就是fetch,如果顺利的话,直接改变了视图。
转载请注明本文来自Tommy.Yu的博客,谢谢!
backbone模型层浅析的更多相关文章
- 再探 Ext JS 6 (sencha touch/ext升级版) 变化篇 (编译命令、滚动条、控制层、模型层、路由)
从sencha touch 2.4.2升级到ext js 6,cmd版本升级到6.0之后发生了很多变化 首先从cmd说起,cmd 6 中sencha app build package不能使用了,se ...
- thinkphp模型层Model、Logic、Service讲解
thinkphp模型层Model.Logic.Service讲解 时间:2014-08-24 15:54:56 编辑:一切随缘 文章来源:php教程网 已阅读:771 次 js特效 ...
- Tp框架之模型层
数据模型层是专门针对数据库来操作的 我们在home模块用一下数据模型层 先把配置修改好 我们先来打开这个文件 然后再打开think里面的主配置,把那里面关于数据库的部分,复制到home下的配置文件,然 ...
- tp框架-----Model模型层
1.Model模型层是用来做什么的呢? 主要是用来做操作数据库访问的.也就说明TP框架自带了一种访问数据库的方式,使用的是Model模型. 2.Model模型怎样使用呢? 要使用Model模型层访问数 ...
- 64、django之模型层(model)--建表、查询、删除基础
要说一个项目最重要的部分是什么那铁定数据了,也就是数据库,这篇就开始带大家走进django关于模型层model的使用,model主要就是操纵数据库不使用sql语句的情况下完成数据库的增删改查.本篇仅带 ...
- 67、django之模型层(model)--查询补充及mookie
本篇导航: F查询与Q查询 cookie 一.F查询与Q查询 1.以Book表为例 class Book(models.Model) : title = models.CharField(max_le ...
- 【Django】模型层说明
[Django模型层] 之前大概介绍Django的文章居然写了两篇..这篇是重点关注了Django的模型层来进行学习. ■ 模型定义 众所周知,Django中的模型定义就是定义一个类,其基本结构是这样 ...
- WEB框架-Django框架学习(二)- 模型层
今日份整理为模型层 1.ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库, ...
- django 模型层(2)
Django 模型层(2) 多表操作---模型之间的关系 1 一对一:作者----作者详细信息 2 一对多:书籍----出版社 3 多对多:书籍----作者 一 创建模型(主键(id)自动创建) 没 ...
随机推荐
- C++ typedef用法小结 (※不能不看※)
C++ typedef用法小结 (※不能不看※) 第一.四个用途 用途一: 定义一种类型的别名,而不只是简单的宏替换.可以用作同时声明指针型的多个对象.比如:char* pa, pb; // 这多数不 ...
- Lua 之 userdata
Lua 之 userdata 在Lua中可以通过自定义类型(user data)与C语言代码更高效.更灵活的交互,从而扩展Lua能够表达的类型. full userdata full userdata ...
- Effective Objective-C 2.0 — 第五条用枚举表示状态、选项、状态码 (未看完)
枚举是一种常量命名方式.某个对象所经历的各种状态就可以定义为一个简单的枚举集.(enumeration set) 编译器会为枚举分配一个独有的编号,从0开始,每个枚举递增1.实现枚举所用的数据类型取决 ...
- knockout-validation不自动插入错误消息
<div data-bind="validationOptions:{insertMessages:false}"> <div class="valid ...
- Redis for .NET 系列之实现分页需求
代码笔记: var tableName = "Table1"; redisClient.AddItemToSortedSet(tableName, ); redisClient.A ...
- Ajax异步刷新地址栏
公司项目后台使用现成的UI框架,DevExpress,jqGrid,XXXUI之类的,这些展示数据列表的控件/插件,基本是异步的. 这倒也好,有变化也只是数据那一块变化,不会重新加载整个页面. 但是, ...
- MySql避免重复插入记录
今天用python抓取数据入库需要避免重复数据插入,在网上找了一些方法: 方案一:使用ignore关键字 如果是用主键primary或者唯一索引unique区分了记录的唯一性,避免重复插入记录可以使用 ...
- checkstyle配置文件说明
属性说明 basedir代码所在的位置 AbstractClassNameformat: 定义抽象类的命名规则 PackageNameformat: 定义包名的命名规则 TypeNameformat: ...
- linux 关机命令总结
linux下常用的关机命令有:shutdown.halt.poweroff.init:重启命令有:reboot.下面本文就主要介绍一些常用的关机命令以及各种关机命令之间的区别和具体用法. 首先来看一下 ...
- Emoji表情符号录入MySQL数据库报错的解决方案(MySQL utf8与utf8mb4区别)
本文转自:http://blog.itpub.net/26230597/viewspace-1243233/前言:手机app应用评论的时候,恢复表情符号,提示失败. 1,查看tomcat后台日志,核心 ...