@tracked 状态下的数组或对象在 Glimmer 组件中,当您更改数组/对象中的某个成员时,不会触发更新。
此主题讨论了一些选项 以使您的视图正确更新。我想添加另一种方法,因为那里讨论的选项对我不起作用。
我的解决方法是:与其一次分配一个元素,不如一次性分配整个数组。这种相同的策略也适用于对象或任何其他数据结构。
在 Glimmer 组件中:
@tracked categories = [];
constructor() {
super(...arguments);
this.loadAllData();
}
async loadAllData() {
const categories = []
const waiting = this.siteSettings.county_fence_print_categories.split('|').map(async (cid, index) => {
const data = await this.loadData(`/c/${cid}/l/latest.json?period=monthly`)
return data.topic_list.topics
})
// 这是需要注意的行
// 因为我们直接对数组进行赋值,它将触发更新。
// 而推送到数组或按索引设置元素则不会触发更新。
this.categories = await Promise.all(waiting)
}
async loadData(url) {
try {
const data = await ajax(url);
//console.log(`Got data for ${url}:`, data)
return data
} catch (error) {
console.error(`Failed to get data for ${url}:`, error);
}
}
您也可以通过部分更新来完成此操作。重要的是让 JavaScript 检测到正在分配不同的值。因此,虽然 .concat 会起作用,因为它会生成一个与旧数组不同的新数组:
this.categories = this.categories.concat(newValue)
使用 .push 可能不会起作用,因为结果数组与原始数组具有相同的对象 ID,因此 JavaScript 认为没有任何变化。
this.categories.push(newValue)
this.categories = this.categories
如果您正在处理一个对象,生成新对象的简单方法是使用 Object.assign() - 它会创建一个新的、扩展的数据结构,就像上面的 .concat 一样。
对于 Vue.js 等其他框架来说,这早已是常态——您可以找到关于此的讨论,可以追溯到 10 年前。这与 JavaScript 中的实现方式有关,可能是父对象上的 getter/setter 或 observe API。但如果它是通过 observe API 实现的,那么对于这种行为的借口就少一些了,因为可以轻松切换 subtree 标志…也许他们出于性能考虑而选择不这样做,当观察深度嵌套的结构时?但跑题了,这些是底层库的实现细节。上面的解决方法应该可以帮助您解决这个问题。