我对骨干网还很陌生,我也遇到了同样的问题。
经过一些研究后,我发现了一些帖子,可以更清楚地解释为什么会发生这种情况,最终事情开始变得有意义:
问题1 https://stackoverflow.com/questions/7325004/backbone-js-set-model-array-property
问题2 https://stackoverflow.com/questions/8491546/models-change-event-wont-fire-when-updating-an-array
核心原因与引用相等与集合/成员相等的概念有关。看来,在很大程度上,引用相等是主干用来确定属性何时发生更改的主要技术之一。
我发现如果我使用生成新引用的技术(例如 Array.slice() 或 _.clone()),则可以识别更改事件。
例如,以下代码不会触发该事件,因为我正在更改相同的数组引用:
this.collection.each(function (caseFileModel) {
var labelArray = caseFileModel.get("labels");
labelArray.push({ Key: 1, DisplayValue: messageData });
caseFileModel.set({ "labels": labelArray });
});
虽然此代码确实触发了事件:
this.collection.each(function (caseFileModel) {
var labelArray = _.clone(caseFileModel.get("labels")); // The clone() call ensures we get a new array reference - a requirement for the change event
labelArray.push({ Key: 1, DisplayValue: messageData });
caseFileModel.set({ "labels": labelArray });
});
注:根据下划线API http://underscorejs.org/#clone, _.clone() 通过引用复制某些嵌套项。不过,根/父对象是克隆的,因此它对于骨干网来说可以很好地工作。也就是说,如果您的数组非常简单并且没有嵌套结构,例如[1,2,3]。
虽然我上面改进的代码触发了更改事件,但以下代码没有触发更改事件,因为我的数组包含嵌套对象:
var labelArray = _.clone(this.model.get("labels"));
_.each(labelArray, function (label) {
label.isSelected = (_.isEqual(label, selectedLabel));
});
this.model.set({ "labels": labelArray });
为什么这很重要?经过仔细调试后,我注意到在迭代器中我引用了存储的相同对象引用主干。换句话说,我无意中触及了模型的内部并发生了一些翻转。当我调用 setLabels() 时,backbone 正确地识别出没有任何变化,因为它已经knew我翻转了一点。
环顾四周后,人们似乎普遍认为 JavaScript 中的深度复制操作确实很痛苦——没有内置的东西可以做到这一点。所以我这样做了,这对我来说效果很好 - 一般适用性可能会有所不同:
var labelArray = JSON.parse(JSON.stringify(this.model.get("labels")));
_.each(labelArray, function (label) {
label.isSelected = (_.isEqual(label, selectedLabel));
});
this.model.set({ "labels": labelArray });