vue3 设计一个完善的响应式系统(进击版本)

大哥大姐们,我想请教下,vue3 设计一个完善的响应式系统(进击版本)
最新回答
浮华沧桑

2026-10-21 10:46:18

在 Vue3 的响应式系统设计中,为了实现一个更完善的进击版本,我们需要解决当前代码中的几个关键问题,并增强其功能性和健壮性。以下是对原代码的改进和扩展:

1. 副作用函数注册机制

当前代码使用全局变量 activeEffect 来存储当前激活的副作用函数,这种方式在复杂应用中可能会导致问题,比如多个副作用函数同时执行时的冲突。我们可以引入一个副作用函数栈来管理:

// 副作用函数栈const effectStack = [];// 当前激活的副作用函数let activeEffect;// effect 函数用于注册副作用函数function effect(fn, options = {}) { const effectFn = () => { cleanup(effectFn); activeEffect = effectFn; effectStack.push(effectFn); const result = fn(); effectStack.pop(); activeEffect = effectStack[effectStack.length - 1]; return result; }; effectFn.deps = []; effectFn.options = options; if (!options.lazy) { effectFn(); } return effectFn;}// 清理函数,用于在副作用函数重新执行前移除旧的依赖function cleanup(effectFn) { for (let i = 0; i < effectFn.deps.length; i++) { const deps = effectFn.deps[i]; deps.delete(effectFn); } effectFn.deps.length = 0;}2. 依赖收集与触发

改进 track 和 trigger 函数,使其更高效且避免不必要的重复收集:

// 储存副作用函数的桶const bucket = new WeakMap();function track(target, key) { if (!activeEffect) return; let depsMap = bucket.get(target); if (!depsMap) { bucket.set(target, (depsMap = new Map())); } let deps = depsMap.get(key); if (!deps) { depsMap.set(key, (deps = new Set())); } // 避免重复收集 if (!deps.has(activeEffect)) { deps.add(activeEffect); activeEffect.deps.push(deps); }}function trigger(target, key) { const depsMap = bucket.get(target); if (!depsMap) return; const effects = depsMap.get(key); if (!effects) return; // 避免无限递归 const effectsToRun = new Set(); effects.forEach(effectFn => { if (effectFn !== activeEffect) { effectsToRun.add(effectFn); } }); effectsToRun.forEach(effectFn => { if (effectFn.options.scheduler) { effectFn.options.scheduler(effectFn); } else { effectFn(); } });}3. 响应式数据代理

改进 Proxy 的 get 和 set 拦截器,使其更健壮:

// 原始数据const data = { text: 'hello world' };// 对原始数据的代理const obj = new Proxy(data, { get(target, key, receiver) { track(target, key); return Reflect.get(target, key, receiver); }, set(target, key, newValue, receiver) { const oldValue = target[key]; const result = Reflect.set(target, key, newValue, receiver); if (oldValue !== newValue) { trigger(target, key); } return result; }});4. 调度执行

添加调度执行功能,允许自定义副作用函数的执行时机:

// 调度执行函数function scheduler(fn) { console.log('调度执行', fn); // 可以在这里添加异步执行、防抖等逻辑 fn();}// 使用调度执行effect(() => { console.log('effect fn'); document.body.innerText = obj.text;}, { scheduler });5. 完整示例

将以上改进整合到一个完整的示例中:

// 副作用函数栈const effectStack = [];// 当前激活的副作用函数let activeEffect;// effect 函数用于注册副作用函数function effect(fn, options = {}) { const effectFn = () => { cleanup(effectFn); activeEffect = effectFn; effectStack.push(effectFn); const result = fn(); effectStack.pop(); activeEffect = effectStack[effectStack.length - 1]; return result; }; effectFn.deps = []; effectFn.options = options; if (!options.lazy) { effectFn(); } return effectFn;}// 清理函数function cleanup(effectFn) { for (let i = 0; i < effectFn.deps.length; i++) { const deps = effectFn.deps[i]; deps.delete(effectFn); } effectFn.deps.length = 0;}// 储存副作用函数的桶const bucket = new WeakMap();function track(target, key) { if (!activeEffect) return; let depsMap = bucket.get(target); if (!depsMap) { bucket.set(target, (depsMap = new Map())); } let deps = depsMap.get(key); if (!deps) { depsMap.set(key, (deps = new Set())); } if (!deps.has(activeEffect)) { deps.add(activeEffect); activeEffect.deps.push(deps); }}function trigger(target, key) { const depsMap = bucket.get(target); if (!depsMap) return; const effects = depsMap.get(key); if (!effects) return; const effectsToRun = new Set(); effects.forEach(effectFn => { if (effectFn !== activeEffect) { effectsToRun.add(effectFn); } }); effectsToRun.forEach(effectFn => { if (effectFn.options.scheduler) { effectFn.options.scheduler(effectFn); } else { effectFn(); } });}// 原始数据const data = { text: 'hello world' };// 对原始数据的代理const obj = new Proxy(data, { get(target, key, receiver) { track(target, key); return Reflect.get(target, key, receiver); }, set(target, key, newValue, receiver) { const oldValue = target[key]; const result = Reflect.set(target, key, newValue, receiver); if (oldValue !== newValue) { trigger(target, key); } return result; }});// 调度执行函数function scheduler(fn) { console.log('调度执行', fn); fn();}// 执行副作用函数effect(() => { console.log('effect fn'); document.body.innerText = obj.text;}, { scheduler });// 1 秒后修改响应式数据setTimeout(() => { obj.text = 'hello vue3';}, 1000);改进点总结
  1. 副作用函数栈:使用栈结构管理嵌套的副作用函数,避免全局变量冲突。
  2. 依赖收集优化:避免重复收集相同的副作用函数,提高性能。
  3. 触发优化:避免在触发副作用函数时重复执行当前正在执行的副作用函数,防止无限递归。
  4. 调度执行:支持自定义调度执行逻辑,如异步执行、防抖等。
  5. 健壮性:使用 Reflect API 确保代理行为的正确性,并在 set 拦截器中比较新旧值,避免不必要的触发。

通过这些改进,我们实现了一个更完善、更健壮的 Vue3 响应式系统进击版本。