Vue 3 的响应式系统是其核心特性之一,它通过 reactivity 库实现。这个库提供了 reactive、ref、computed 和 effect 等 API。
核心概念
- Reactive: 将对象转换为响应式对象。
- Ref: 将基本类型值包装为响应式对象。
- Computed: 创建基于其他响应式数据的计算属性。
- Effect: 副作用函数,当响应式数据变化时自动执行。
源码结构
以下是 Vue 3 reactivity 库的核心实现,包括 reactive、ref 和 effect 的简化代码。
依赖收集和触发
let activeEffect = null;
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect);
}
}
notify() {
this.subscribers.forEach(sub => sub());
}
}
function effect(fn) {
activeEffect = fn;
fn();
activeEffect = null;
}
Dep 类用于依赖收集和触发更新。effect 函数用于注册副作用函数。
Reactive 实现
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;
}
function reactive(target) {
return new Proxy(target, {
get(obj, key) {
const dep = getDep(obj, key);
dep.depend();
return Reflect.get(obj, key);
},
set(obj, key, value) {
const dep = getDep(obj, key);
const result = Reflect.set(obj, key, value);
dep.notify();
return result;
}
});
}
reactive 函数通过 Proxy 将对象转换为响应式对象。通过 getDep 函数获取或创建依赖对象,并在 get 和 set 拦截器中进行依赖收集和触发更新。
Ref 实现
class RefImpl {
constructor(value) {
this._value = value;
this.dep = new Dep();
}
get value() {
this.dep.depend();
return this._value;
}
set value(newValue) {
if (newValue !== this._value) {
this._value = newValue;
this.dep.notify();
}
}
}
function ref(value) {
return new RefImpl(value);
}
ref 函数将基本类型值包装为响应式对象,通过 RefImpl 类实现。
Computed 实现
class ComputedRefImpl {
constructor(getter) {
this.getter = getter;
this.dep = new Dep();
this._dirty = true;
this._value = undefined;
this.effect = effect(() => {
if (this._dirty) {
this._value = this.getter();
this._dirty = false;
}
});
}
get value() {
if (this._dirty) {
this._value = this.getter();
this._dirty = false;
}
this.dep.depend();
return this._value;
}
}
function computed(getter) {
return new ComputedRefImpl(getter);
}
computed 函数创建基于其他响应式数据的计算属性,通过 ComputedRefImpl 类实现。
示例应用
以下是如何使用上述实现创建响应式对象、引用和计算属性的示例:
const state = reactive({ count: 0 });
effect(() => {
console.log(`Count is: ${state.count}`);
});
state.count++; // 输出: Count is: 1
const countRef = ref(0);
effect(() => {
console.log(`CountRef is: ${countRef.value}`);
});
countRef.value++; // 输出: CountRef is: 1
const doubled = computed(() => state.count * 2);
effect(() => {
console.log(`Doubled count is: ${doubled.value}`);
});
state.count++; // 输出: Count is: 2, Doubled count is: 4
Vue 3 的 reactivity 库通过 Proxy 和依赖收集机制实现了响应式系统。reactive 函数将对象转换为响应式对象,ref 函数将基本类型值包装为响应式对象,computed 函数创建基于其他响应式数据的计算属性。通过这些 API,Vue 3 能够高效地管理和更新视图。