Vue.js中仅修改新增属性无法触发视图更新,如何解决?
这个问题我太熟啦!Vue 2的响应式系统是基于Object.defineProperty实现的,它只会在初始化阶段对已有的属性建立监听。你后来给todo对象新增的added_text属性,压根没被Vue的响应式系统“盯上”,所以视图自然不会更新。而你修改已有的text属性时,因为它一开始就在监听列表里,修改会触发更新,这时候Vue会重新渲染整个todo项,顺带把新增的added_text也显示出来——这就是为啥注释那行打开后两个属性都能更新的原因。
下面给你几个靠谱的解决办法:
1. 使用
this.$set(官方推荐方案) Vue专门提供了Vue.set(在组件内部可以用this.$set)这个API,用来给对象添加响应式属性。它会帮你把新属性纳入响应式系统,同时触发视图更新。
修改你的add_text方法就行:
add_text(item) { const index = this.todos.indexOf(item); this.$set(this.todos[index], 'added_text', 'something else'); }
2. 替换整个todo对象
Vue能检测到数组元素的替换操作(比如用splice,或者生成全新的数组),所以你可以创建一个包含新属性的新对象,然后替换掉原数组里的旧对象:
方法A:用splice替换数组元素
add_text(item) { const index = this.todos.indexOf(item); // 用扩展运算符合并原有属性和新属性,生成新对象 const newTodo = { ...item, added_text: 'something else' }; // 替换原数组中的旧对象 this.todos.splice(index, 1, newTodo); }
方法B:生成全新数组(更符合不可变数据思想)
add_text(item) { const index = this.todos.indexOf(item); const newTodo = { ...item, added_text: 'something else' }; // 用扩展运算符生成新数组,替换原数组 this.todos = [ ...this.todos.slice(0, index), newTodo, ...this.todos.slice(index + 1) ]; }
3. 提前定义好属性(如果能预知需求的话)
如果一开始就知道可能会用到added_text这个属性,那在初始化todos的时候就给每个对象加上它,哪怕值是空字符串也没关系:
data: { todos: [ { text: 'Learn JavaScript', added_text: '' }, { text: 'Learn Vue', added_text: '' }, ] }
这样后续直接给added_text赋值,就能正常触发视图更新了。
内容的提问来源于stack exchange,提问作者Jan Salvador van der Ven
相关产品推荐
相关产品推荐

