zoukankan      html  css  js  c++  java
  • 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的博客,谢谢!

  • 相关阅读:
    【web charting】21个Javascript图表插件程序
    【IOC框架】分析与理解
    【待续】【HTML5】用Canvas标签创建第一张条线图
    【转】大型网站后台架构的演变
    初探Visual C# SQL CLR Database Project
    Js中 关于top、clientTop、scrollTop、offsetTop
    JS的正则表达式
    jquery的extend和fn.extend
    C/C++版数据结构之链表<三>
    C/C++ 一点笔记(2)
  • 原文地址:https://www.cnblogs.com/Tommy-Yu/p/4211552.html
Copyright © 2011-2022 走看看