Vue2实现响应式核心就是Object.defineProperty数据劫持,针对数组重写了数组方法
在Vue2源码中实现响应式考虑到更多情况,以下所写了V2实现响应式的核心逻辑
const data = {
name: 'wft',
age: 18,
info: {
test: '哈哈哈'
},
list: [1, 2, 3, 4, 5]
}
const oldArrProto = Array.prototype;
const newArrProto = Object.create(oldArrProto);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
newArrProto[methodName] = function () { // 1. 更新视图; 2. 执行原来的方法
oldArrProto[methodName].call(this, ...arguments)
console.log('更新视图操作----->>>')
}
})
observer(data)
function observer(target) {
if (typeof target !== 'object' || target === null) {
return target
}
if (target.constructor === Array) { // 如果是数组的话, 就把原型对象修改成重写之后的(主要是为了能够进行更新视图操作)
target.__proto__ = newArrProto
}
for (let key in target) {
defineReactive(target, key, target[key])
}
}
function defineReactive(target, key, value) {
if (typeof value === 'object') { // 多层嵌套递归
observer(value)
}
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (typeof newValue === 'object') {
observer(newValue)
}
if (newValue !== value) {
value = newValue
console.log('更新视图操作----->>>')
}
}
})
}
// data.name = '哈哈哈'
// data.info.test = '1111'
// data.name = {
// name1: 'name1',
// name2: 'name2'
// }
// data.name.name1 = '修改name1'
data.list.push(555)
上面有用到Object.create() ,这个方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__,是为了重写数组上的方法
当我们在data中定义了数据之后,vue2就会自动把它加入到响应式系统中(有对象就深层递归),而且Object.defineProperty能力有限,不能察觉到对象的属性新增(v2中我们使用Vue.set解决),也不能察觉到对象的属性删除(v2中我们使用Vue.delete解决),这些也都是Vue2中的缺点,性能损耗较大
但是Vue3中就很好的避免上述Vue2中的问题,v3是通过proxy代理来实现数据的响应式,并且是当我们真正用到的时候才会将其加入到响应式,并且支持属性的新增和删除