这是我参与「第四届青训营 」笔记创作活动的的第15天
1. vue2.x的响应式
实现原理:
- 对象类型:通过
Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。 - 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', {
get () {
return data.count;
},
set (value) {
data.count = value;
}
})
存在问题
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。需通过数组的api才能实现,如
splice,push等
2. vue3.0的响应式
reactive数据响应实现原理:
- 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写
get和set、属性的添加set、属性的删除deleteProperty等。 - 通过Reflect(反射): 对源对象的属性进行操作。
实现对person对象数据的代理响应:
let person = {
name: "yanghi",
age: 18,
sex: "男"
}
// target 当前对象 prop 当前属性 value 新值
let proxyPerson = new Proxy(person, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
提出问题: 为什么要使用**
Reflect(反射)API** 对原属性进行修改,而不使用Object.defineProperty()API 对原属性进行修改呢 ?
- 为了保持程序的健壮性,因为
Reflect(反射)API 对原属性进行修改返回一个布尔值,可用于判断是否修改成功,即使未修改成功也不会报错。 - 而
Object.defineProperty()API 对原属性进行修改,如果未成功,会产生错误,那么我们需要进行try catch包裹,这样的话我们的代码很糟糕。
如下使用 Object.defineProperty() API 对原属性进行修改:
let person = {
name: "yanghi",
age: 18
}
try{
Object.defineProperty(person, 'name', {
get () {
return person.name;
},
set (value) {
person.name = value;
}
});
Object.defineProperty(person, 'age', {
get () {
return person.age;
},
set (value) {
person.age = value;
}
});
}catch(err){
console.log(err);
}
使用(proxy)代理的好处:
- 可直接新增属性和删除属性,界面会自动更新
- 可直接通过下标修改数组,界面会自动更新
3. reactive 对比 ref
-
从定义数据角度对比:
- ref用来定义:基本类型数据。
- reactive用来定义:对象(或数组)类型数据。
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过
reactive转为代理对象。
-
从原理角度对比:
- ref通过
Object.defineProperty()的get与set来实现响应式(数据劫持)。 - reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- ref通过
-
从使用角度对比:
- ref定义的数据:操作数据需要
.value,读取数据时模板中直接读取不需要.value。 - reactive定义的数据:操作数据与读取数据:均不需要
.value。
- ref定义的数据:操作数据需要