标签: fetch

  • Backbone.Collection.fetch小优化一则

    使用Backbone开发无限滚动应用的时候,必然会用到Collection.fetch()加载后面的内容。在Backbone的设计中,fetch之后,Collection会把取回来的内容“智能合并”到当前结果集中,该添加的添加、该修改的修改、该删除的删除,并且触发不同的事件。

    这样就会带来一个问题,因为我们知道,向DOM树当中添加元素的次数越少越好,因为每次添加都会导致重新计算布局和重绘;但是Backbone又是依次触发add事件的,怎么办呢?

    其实也容易,因为set完成后,Collection还会广播sync事件,所以我们可以在响应add事件时拼装jquery对象,响应sync事件时再把它们添加到DOM树中。代码如下:

    var InfinityScroll = Backbone.View.extend({
      initialize: function () {
        this.collection.on('add', this.collection_addHandler, this);
        this.collection.on('sync', this.collection_syncHandler, this);
      },
      collection_addHandler: function (model) {
        var html = this.template(model.toJSON());
        this.fragment = this.fragment ? this.fragment.add(html) : $(html);
      },
      collection_syncHandler: function () {
        if (this.fragment) {
          this.$el.append(this.fragment);
          this.fragment = null;
        }
      }
    });
    
  • 重写Backbone.js的加载动作

    居然在Google里找不到类似的情况,难道只有我一个人会有这种疑问么……

    我在使用Backbone的时候,遇到一个问题:我需要用 Backbone.Model 或者 Backbone.Collection 来加载一些远程数据,一般来说都是静态文件,比如HTML或者XML,既不满足RESTful,也不是JSON;虽然不很符合Backbone的要求,不过因为是静态的,所以我觉得ajax应该都没问题。当我按照这个思路写下去,一般就是这样:

    var MyModelClass = Backbone.Model.extend({
      url: 'config.xml',
      parse: function (response) {
        console.log(response);
      }
    });
    var model = new MyModelClass();
    model.fetch();
    

    但是运行之后,我发现被覆盖的 parse 没有执行。然而查看网络,目标文件已经被正常加载。作为Backbone的初学者,也不知道问题出在哪里。不得已找来源码跟踪,发现Backbone实现 fetch 是委托给 Backbone.sync 方法,但是在实现的时候会把数据格式设置为json:

    // http://documentcloud.github.com/backbone/backbone.js
    // Default JSON-request options.
    var params = {type: type, dataType: 'json'};
    

    而 jQuery 1.4版之后,会对返回的数据格式进行验证,如果不符合就抛出异常。所以当我那些不是 JSON 但声明是 JSON 的数据加载完毕后,jQuery 就会抛出异常,于是覆盖的parse也就不执行了。

    如果远程数据不是JSON,需要覆盖数据加载逻辑时,就应该覆写 .fetch(),比如这样:

    var MyModelClass = Backbone.Model.extend({
      url: 'config.xml',
      fetch: function () {
        $.ajax({
          url: this.url,
          context: this,
          success: this.parse
        });
      }
      parse: function (response) {
        console.log(response);
      }
    });
    
    var model = new MyModelClass();
    model.fecth();
    

    我认为不应该重写 .sync(),因为实际应用中,不同的 Model 和 Collection 可能要提供加载远程数据、加载模板、与 LocalStorage 交互等不同的功能,重写sync也很难满足需要。所以不妨直接在不同的类里面覆盖各自的fetch、save方法。


    这篇文章还有人看,那就更新一下。(2016-11-25)

    Backbone 用的久了,发现它其实设计了更好的办法解决这种问题:调用 .fetch() 的时候传递 options

    // 以下代码通过继承并覆写 .fetch() 方法,告知 jQuery 返回数据类型是 XML
    var MyModel = Backbone.Model.extend({
      fetch: function (options) {
        options = options || {};
        options.dataType = 'xml';
        return Backbone.Model.prototype.fetch.call(this, options);
      },
      parse: function (response) {
        console.log(response); // jQuery 会帮我们分析这个 XML
      }
    });
    

    这种方式比上面的好得多。