万神劫

万物天地为剑,神鬼妖邪为剑
劫波万渡,宇宙苍穹尽为剑
是为万神劫!

4条评论 2012-07-19

Backbone 的 silent:true 陷阱

2013-04-10 更新
最近翻阅 Backbone 的更新日志,发现在 0.9.10 里面已经修复了这个问题

Passing {silent:true} on change will no longer delay individual "change:attr" events, instead they are silenced entirely.


最近在使用 Backbone 时,遇到一个非常奇怪的问题,与 Model#set 方法的 silent:true 参数有关
不罗嗦,大家直接看代码

var User=Backbone.Model.extend();
var u=new User();
u.on("change:name", function(){
  console.log('name changed');
});
u.set('name', 'sb');
u.set('name', '250', {silent:true});
u.set('age', 23);

大家觉得以上代码的执行结果会是怎样?是不是认为只会打印一条 name changed
呵呵,实际上这段代码打印了两条记录,不信看这里 http://jsbin.com/apulaf

而且经过多次试验,我发现上面代码的最后那一句,不管你 set 什么属性给对象,都会触发它的 change:name 事件
也就是说,即使你在 set 属性时传递了 {silent:true} 参数,但如果稍后又改变了此对象的其他属性,那么前面属性对应的 change 事件依然会被触发!

怎么会这样呢?是 Backbone 的 Bug 吗?它的官方文档上可是白纸黑字写着这样一段啊

model.set(attributes, [options])
Set a hash of attributes (one or many) on the model.
If any of the attributes change the models state, a "change" event will be triggered, unless {silent: true} is passed as an option.

相信大部分人看完这段都会认为,{silent:true} 参数的作用是用于不触发相应的 change 事件
可是凡人!你们错了! 看这个 Issue ,仔细看作者的回复:

Silent allows you to batch up your changes, and delay the change event ... not to pretend like they never occurred.

看明白了吗?作者说,其实 silent 的作用不是用于永远阻止这次修改事件的触发...而是...用于批量修改大量属性时,延迟触发前面的某次修改事件...
好吧,我已吐槽不能,既然这样,你们能稍微更新下文档吗?把 silent 的作用讲讲清楚啊童鞋!!不要再让人误人子弟了啊!!!
那么,回到正题,如果想要修改对象的属性,而又确实不想触发对应的事件,该怎么做呢?
我临时想到的办法是,直接修改 attributes 属性,像这样

// 修改单个属性
u.attributes.name = '250'

// 修改多个属性
_.extend(u.attribute, {
  name: '250',
  age: 23
})

不过不确定是否会带来某些副作用,等我仔细研究下 Backbone 的代码再来更新此文吧

comments powered by Disqus