Vue 3 的响应式系统与 Vue 2 有显著不同,Vue 3 使用了 Proxy 替代了 Vue 2 中的 Object.defineProperty
,这使得 Vue 3 的响应式系统更加灵活和强大
Vue 3 响应式原理的核心
-
Proxy:
- Vue 3 使用
Proxy
来拦截对象的操作(如读取、赋值、删除等)。 Proxy
可以监听整个对象,而不需要像Object.defineProperty
那样递归遍历每个属性。
- Vue 3 使用
-
Reflect:
Reflect
是一个内置对象,提供了与Proxy
拦截操作对应的方法。- Vue 3 使用
Reflect
来执行默认行为(如读取属性值、设置属性值等)。
-
依赖收集与派发更新:
- Vue 3 仍然使用依赖收集和派发更新的机制,但实现方式更加简洁。
- 通过
effect
函数(类似于 Vue 2 的watchEffect
)来追踪依赖。
简单实现 Vue 3 响应式系统
以下是一个简化版的 Vue 3 响应式系统实现:
javascript
// 存储当前正在执行的 effect 函数
let activeEffect = null;
// 依赖收集器
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect);
}
}
notify() {
this.subscribers.forEach(effect => effect());
}
}
// 创建响应式对象
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
const dep = getDep(target, key);
dep.depend(); // 依赖收集
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
const dep = getDep(target, key);
const result = Reflect.set(target, key, value, receiver);
dep.notify(); // 派发更新
return result;
}
});
}
// 存储每个对象的每个 key 对应的 Dep 实例
const targetMap = new WeakMap();
function getDep(target, key) {
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let dep = depsMap.get(key);
if (!dep) {
dep = new Dep();
depsMap.set(key, dep);
}
return dep;
}
// effect 函数,用于追踪依赖
function effect(fn) {
activeEffect = fn;
fn(); // 执行函数,触发 getter,收集依赖
activeEffect = null;
}
// 测试
const state = reactive({ count: 0, list: [1, 2, 3] });
effect(() => {
console.log('Count:', state.count);
});
effect(() => {
console.log('List:', state.list);
});
state.count++; // 输出: Count: 1
state.list.push(4); // 输出: List: [1, 2, 3, 4]
代码解析
-
reactive
函数:- 使用
Proxy
包装对象,拦截get
和set
操作。 - 在
get
中调用dep.depend()
收集依赖。 - 在
set
中调用dep.notify()
派发更新。
- 使用
-
Dep
类:- 用于存储依赖(
effect
函数)。 - 提供
depend
方法收集依赖,notify
方法派发更新。
- 用于存储依赖(
-
effect
函数:- 类似于 Vue 2 的
watchEffect
,用于执行副作用函数并追踪依赖。 - 在执行过程中,
activeEffect
会被设置为当前函数,从而在get
中被收集。
- 类似于 Vue 2 的
-
targetMap
和getDep
:targetMap
是一个WeakMap
,用于存储每个对象及其对应的depsMap
。depsMap
是一个Map
,用于存储每个 key 对应的Dep
实例。getDep
函数用于获取或创建Dep
实例。
Vue 3 的优势
-
更好的性能:
Proxy
可以监听整个对象,而不需要递归遍历每个属性。- 只有在访问属性时才会创建
Dep
,减少了初始化开销。
-
更强大的功能:
Proxy
可以拦截更多操作,如deleteProperty
、has
等。- 支持数组的
push
、pop
等操作,无需额外处理。
-
更简洁的代码:
- 依赖收集和派发更新的逻辑更加清晰和集中。
总结
Vue 3 的响应式系统基于 Proxy
和 Reflect
,相比 Vue 2 的 Object.defineProperty
,具有更好的性能和更强大的功能。通过 effect
函数追踪依赖,Dep
类管理依赖,Proxy
拦截操作,Vue 3 实现了一个高效且灵活的响应式系统。简化版的 Vue 3 的核心原理。