在使用 Backfire 的模型进行了相当广泛的工作后,我对此有几个想法。我希望他们中的一些人能为您的项目提供好的想法。
将心智模型更改为实时环境
首先,摆脱知道“所有数据已加载”的心态,假设这会像我早期一样困扰您。我们现在处于实时环境中。只需从零开始,将每条传入的记录都视为更新。这节省了大量尝试处理状态的时间和精力。
延迟渲染和 DOM 绑定
现在有了 Backbone,我经常发现自己想做一个惰性渲染。也就是说,我想在逻辑上处理以下情况:
- 立即开始收集数据,但在调用 render 之前不要显示它
- 显示“正在加载”消息,直到出现一些数据
- 当多条记录靠近时,不要为每条记录重新渲染
频繁更改数据的一个很好的解决方案是 Backbone.ModelBinder 的CollectionBinder 工具,它单独操作 DOM 的每个节点,而不是重新渲染所有记录。他们的网站上有很多例子,所以我不会在这里详细介绍。
Debounce 作为一种快速而肮脏的解决方案
Underscore 的debounce方法对于不需要数据绑定的所有复杂性的小规模 DOM 操作来说是一个很好的解决方案。等待大约 250 次的去抖动对我来说效果很好,确保渲染总是发生在数据更改上,但只有在我们连续获得大量更新时才会发生一次。
假设我们创建了一个扩展 Backbone.Firebase.Collection 的集合,那么我们可以执行以下操作:
var View = Backbone.View.extend({
initialize: function() {
this.listenTo( this.collection, 'add remove', _.debounce(_.bind(this.dataChanged, this), 250) );
},
render: function() {
this._isRendered = true;
/* do all my rendering magic here */
},
dataChanged: function() {
// wait until render has been called at least once
// if so, re-render on any change
this._isRendered && this.render();
}
});
使用 Deferred 等待加载的数据
在我的 Backfire 实现中,我添加了一个有状态的方法,它会在第一次加载时通知我。我使用 jQuery 的Deferred对象做到了这一点。然后我只听集合触发一个sync
事件:
this.collection.once('sync', /* data is loaded */);
Firebase 的一个好处是最初的 Firebase.on('child_added'...) 结果(现有记录)往往会出现在一个很好的大块中——一个接一个。所以作为一个额外的好处,我使用 debounce 使我的“加载”方法在初始集群完成后触发,所以我没有得到一条记录,调用加载,然后立即需要对一系列更新采取一些行动。
由于这是特定于实现的,我将在这里有点抽象,但这是它的要点:
// inside my wrapper for Backbone.Firebase.Collection
this.ready = $.Deferred();
// debounce our ready listener so it fires on the tail end of
// the initial update clump which reduces the number of update
// calls on the initial load process considerably
this.readyFn = _.debounce(this.ready.resolve, 250);
// start monitoring for the first series of updates
// this would need to be invoked before the sync is established
this.on( 'add', this.readyFn );
// wait for the data to come in
this.ready.always( _.bind(function() {
// when the ready promise is fulfilled, we can turn off the listener
this.off('add', this.readyFn);
// this is where we trigger the listener event used above
this.trigger('sync');
}, this) );
我会小心使用这个解决方案。我发现在大多数情况下,我可以通过忘记初始加载并将所有内容初始化为空,然后将所有内容视为更新来大大简化事情。
我只在不存在数据时需要显示一些替代视图的情况下使用它(例如入门说明)。