vue2响应式原理
对象类型: 通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)
// Object.defineProperty(obj, prop, descriptor)
// obj 要定义属性的对象。
// prop 要定义或修改的属性的名称或 Symbol 。
// descriptor 要定义或修改的属性描述符。
// 返回值 被传递给函数的对象。
let person = {
name: '张三',
age: 15,
}
let p = {}
Object.defineProperty(p, 'name', {
configurable: true, //配置这个属性表示可删除的,否则delete p.name 是删除不了的 false
get() {
//有人读取name属性时调用
console.log('读取');
return person.name
},
// 属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。
set(value) {
//有人修改时调用
console.log('有人修改');
person.name = value
}
})
console.log(p.name);
p.name = "李四"
console.log(p.name);
控制台结果
Vue3响应式原理
let person = {
name: '张三',
age: 15,
}
//我们管p叫做代理数据,管person叫源数据
const p = new Proxy(person, {
//target代表的是person这个源对象,propName代表读取或者写入的属性名
get(target, propName) {
console.log('有人读取了p上面的propName属性')
return target[propName]
},
//不仅仅是修改调用,增加的时候也会调用
set(target, propName, value) {
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了`)
target[propName] = value
},
deleteProperty(target, propName) {
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了`)
return delete target[propName]
},
isExtensible(target, propName) {
console.log(propName);
return target[propName]
}
})
//映射到person上了,捕捉到修改,那就是响应式啊
console.log(p.name);// 读取
p.name = "李四" // 修改
console.log(p.name);
delete p.name // 删除
p.sex = "男" // 新增 也调set
控制台结果:
vue3底层源码不是我们上面写的那么low,实现原理一样,但是用了一个新的方式
通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等等。
通过Reflect(反射):对被代理对象的属性进行操作
MDN文档中描述的Proxy与Reflect:可以参考对应的文档
最终Vue3的响应式原理:
let person = {
name: '张三',
age: 15,
}
//我们管p叫做代理数据,管person叫源数据
const p = new Proxy(person, {
//target代表的是person这个源对象,propName代表读取或者写入的属性名
get(target, propName) {
console.log('有人读取了p上面的propName属性')
// Reflect.get() 类似于 target[propName] 读取属性值 只是通过函数执行
return Reflect.get(target, propName)
},
//不仅仅是修改调用,增加的时候也会调用
set(target, propName, value) {
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了`)
Reflect.set(target, propName, value)
},
deleteProperty(target, propName) {
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了`)
console.log(Reflect.deleteProperty(target, propName));
return Reflect.deleteProperty(target, propName)
}
})
console.log(p.name);
p.name = "李四"
delete p.name
控制台如下: