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的博客,谢谢!

  • 相关阅读:
    Codeforces 451A Game With Sticks
    POJ 3624 Charm Bracelet
    POJ 2127 Greatest Common Increasing Subsequence
    POJ 1458 Common Subsequence
    HDU 1087 Super Jumping! Jumping! Jumping!
    HDU 1698
    HDU 1754
    POJ 1724
    POJ 1201
    CSUOJ 1256
  • 原文地址:https://www.cnblogs.com/Tommy-Yu/p/4211552.html
Copyright © 2011-2022 走看看