响应式数据的本质
vue实现响应式主要是通过 JavaScript 的 object.defineProperty 或者 vue3 的 proxy 来实现的
Object.defineProperty vs Proxy
-
功能范围
-
definproperty
- 只能对单个属性进行定义和控制
- 需要为每个需要响应的属性单独定义 getter 和 setter
- 只能劫持对象现有的属性 无法对新添加的属性进行响应式处理
-
proxy
- 可以拦截多种操作 包括 属性访问 赋值 删除 枚举 函数调用等
- 可以对整个对象进行代理 所有属性都可以拦截
- 可以动态地处理新添加的属性
-
-
性能
-
definProperty
- 需要为每个属性单独定义 getter 和 setter 可能会导致性能开销较大 尤其式在处理大量属性时
-
proxy
- 由于可以一次性代理整个对象 性能上更优 尤其是需要动态添加属性的情况下
-
-
兼容性
-
definproperty
- 在es5中引入 兼容性较好 支持较早的浏览器
-
proxy
- 在es6中引入兼容性较差 某些旧版浏览器不支持
-
Proxy实现
// 当前活跃的依赖
let activeRelyOn = null;
// 依赖管理器
class Dep {
// 存储依赖的 watcher
constructor() {
this.subrelyon = new Set();
}
// 添加依赖
depAddRelyOn() {
if (activeRelyOn){
this.subrelyon.add(activeRelyOn);
}
}
// 通知依赖更新
depRelyOnUpdate(){
this.subrelyon.forEach(affect=>affect())
}
}
// 响应函数
let responseReactive = ()=>{
// 创建一个依赖管理器
const dep = new Dep();
return new Proxy(target,{
get(target,key){
// 收集依赖
dep.depAddRelyOn();
// 返回属性值
return Reflect.get(target,key);
},
set(target,key,value){
//
console.log(target);
const relyOnKey = target[key];
// 设置属性值
const result = Reflect.set(target.key,value);
if(relyOnKey !== value){
dep.depRelyOnUpdate();
}
// 返回设置结果
return result;
}
})
}
let ecflect = (fn)=>{
// 设置当前活跃的 wetcher
activeRelyOn = fn;
fn();
// 清空当前活跃的wetcher
activeRelyOn = null;
}
// 创建一个响应对象
let relyOnObj = {
name:"测试对象",
}
// 使用ecflect监听当前活跃watcher的变化
ecflect(()=>{
console.log(`Name: ${relyOnObj.name}`);
})
// 修改响应式对象的属性
relyOnObj.name = 'Vue 3';