在 Vue2 中,响应式是通过 Object.defineProperty
对对象的每个属性进行数据劫持(getter 和 setter)来实现的。
但是,对于数组Vue 并不会对每个元素进行单独的 get
/set
拦截。Vue 只能通过重写数组的原型方法(例如 push
、pop
等)来检测数组的变化。
因此,当你直接通过索引修改数组的某个元素时,Vue 无法监听到这个变化。
let obj = {
a:1,
b:2,
c :{
n:3
},
d:['1','2','3']
}
let oldArrayPrototype = Array.prototype
let proto = Object.create(oldArrayPrototype)
Array.from(['push','shift','pop','unshift']).forEach((method) =>{
// 函数劫持重写函数
proto[method] = function(){
oldArrayPrototype[method].call(this,...arguments)
updateView()
}
})
// 更新视图
function updateView() {
}
// 递归观察
function observer(target){
if(Array.isArray(target)){
// 重写数组原型
target.__proto__ = proto
return
}
for(let key in target){
defineReactive(target,key,target[key])
}
}
// 数据更新
function defineReactive(target,key,value) {
if(typeof value === 'object' && value !==null){
observer(value)
}
// 数据劫持
Object.defineProperty(target,key,{
get(){
return value;
},
set(newVal){
if(newVal !== value){
value = newVal
updateView()
}
}
})
}
observer(obj)
obj.d.push('4')