数据响应式:就是当数据发生变化时,vue会通知到使用该页面的代码。页面UI也发生相应的变化。
Vue2中的响应式原理:
通过object.defineProperty对对象的属性的读取、修改进行拦截(数据劫持),调用get和set对数据进行操作后返回。
数组类型:通过重写一系列数组的方法来实现拦截。
Object.defineProperty在获取对象属性和修改对象属性的时候会实现响应式,但是缺点是无法观察到新增数据和删除数据的变化。
Js 简易实现vue2响应式:
//源数据
let person = {
name:'小明',
age:58,
}
//模拟vue2中实现响应式
let p = {};
for (let key in person){
Object.defineProperty(p,key,{
get(){
console.log(`我读取了${key}属性`);
return person.key;
},
set(value){
console.log(`我修改了${key}属性,去更新界面`);
person.key = value;
}
})
}
VUE3实现响应式:
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
通过Reflect(反射): 对源对象的属性进行操作。
Js 简易实现vue3响应式:
//源数据
let person = {
name:'小明',
age:58,
}
//vue3 proxy代理实现响应式原理
const p = new Proxy(person,{
//target:原对象 propName:参数名
get(target,propName){
console.log(`有人读取了p身上的${propName}属性${target}`);
return target[propName];
},
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性`);
return target[propName] = value;
},
deleteProperty(target,property){
console.log(`有人删除了p身上的${propName}属性`);
return delete target[property];
}
})
此时,响应式的实现大致完成,但还不是最完美的解决方案。例如:
const p1 = Object.defineProperty(person, "name", {
get() {
return "李四"
}
})
// 此处出现异常
const p2 = Object.defineProperty(person, "name", {
get() {
return "王五"
}
})
console.log(person)
上述代码,在重复对person 的 name 属性进行操作后,直接出现异常,导致单线程挂掉。如要解决以上问题,则需要用 try...catch 对异常进行捕获,非常的麻烦。
此时,我们可以使用Reflect 处理这种问题。
Reflect 是 window 上的内置构造函数,目前 ECMA 组织正尽力于把 Object 内置构造函数上的有用的 api 移植到 Reflect 之上,如:set()、get()、deleteProperty()…
与Object 不同的是,Reflect 会返回一个布尔值,可以更灵活地捕捉到异常,判断操作是否成功,从而进一步对错误进行抛出。
结合以上所述,实现响应式可优化为:
const p = new Proxy(person, {
get(target, propName) {
return Reflect.get(target, propName)
},
set(target, propName, value) {
return Reflect.set(target, PropName, value)
},
deleteProperty(target, propName) {
return Reflect.deleteProperty(target, PropName)
}
})