VUE 数组无法触发视图更新
首先请看下面代码:
new Vue({
el: "#app",
data: {
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
},
methods: {
toggle: function(todo){
todo.done = !todo.done
},
//视图会更新
setArr:function(){
this.todos = [
{ text: "Learn JavaScript", done: false }
]
},
//视图不会更新
test:function(){
this.todos[0] ={ text: "ok", done: false }
},
//通过调用VUE 自身$set 方面可触发手动更新
setTest:function(){
this.$set(this.todos,0,{ text: "ok", done: false })
}
},
mounted(){
}
})
这个坑要从VUE 实现数据双向绑定原理说起:
VUE 采用 Object.defineProperty 来拦截监听数据的变化,而Object.defineProperty只能拦截监听到某个具体属性的变化,而拦截不到具体属性中某个值的变化,下面我通过代码来验证一下:
可见当直接修改todos 属性时 才会触发监听todos 的set 方法进行拦截。如直接修改todos[0]下某个值时无法触发set,故无法监听触发set 方法 导致VUE 的订阅器无法触发通知去更新视图view
var data = {
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
}
var value = ''
Object.defineProperty(data, 'todos', {
set: function (newValue) {
value = newValue;
console.log('触发视图更新' + JSON.stringify(value));
},
get: function () {
return value
}
})
data.todos = [
{ text: "Learn JavaScript", done: false } // 拦截调用set方法 你取了一个书名叫做 Object
]
console.log(data.todos) //[{ text: "Learn JavaScript", done: false }]
data.todos[0] = { text: "ok", done: false } //无法拦截到
我们在手动模拟$set 方法触发更新一下:
var data = {
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
}
//模拟实现 VUE.$set this.$set(this.todos,0,{ text: "ok", done: false })
let setFuncion = (target, changeKey, values) => {
var obj = {};
target.forEach((item,key)=>{
/**
* Object.defineProperty 只能监听对象 所以首先将数组转换成key,val 对象,在进行监听
*/
obj[key] = item;
/**
* 转换完成之后 对 对象的key 进行监听
*/
Object.defineProperty(obj, key, {
set: function (newValue) {
target[changeKey] = newValue
console.log('触发更新视图' + JSON.stringify(newValue));
},
get: function () {
return target[changeKey]
}
})
});
obj[changeKey] = values
}
//调用set方法,触发更新视图{"text":"ok","done":false}
setFuncion(data.todos, 0, { text: "ok", done: false })
console.log(data.todos)
/* [
{ text: "ok", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
] */