0
点赞
收藏
分享

微信扫一扫

Vue3 响应式原理详解

Vue3 的组合式 API 和响应式系统是其核心亮点,通过 Proxy 实现的响应式机制相比 Vue2 的 Object.defineProperty 有了质的提升。本文将深入解析 Vue3 响应式系统的实现原理,结合代码示例帮助理解其工作机制。

一、Vue3 响应式系统的核心组件

1. 三大核心模块

  • reactive:创建响应式对象
  • effect:副作用函数,依赖收集的载体
  • track:依赖收集
  • trigger:触发更新

2. 响应式系统的工作流程

  1. 通过 reactive 创建响应式对象
  2. effect 注册副作用函数并执行
  3. 副作用函数访问响应式对象属性时触发 track 收集依赖
  4. 响应式对象属性变更时触发 trigger 通知更新

二、响应式系统的基本实现

1. 基础响应式对象实现

// 存储依赖关系的 WeakMap
const targetMap = new WeakMap();

// 收集依赖
function track(target, key) {
  // 获取当前激活的 effect
  if (!activeEffect) return;
  
  // 获取目标对象的依赖映射
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  
  // 获取属性的依赖集合
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }
  
  // 将当前 effect 添加到依赖集合中
  dep.add(activeEffect);
  // 反向收集,便于清理
  activeEffect.deps.push(dep);
}

// 触发更新
function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  
  // 获取属性的依赖集合
  const dep = depsMap.get(key);
  if (!dep) return;
  
  // 执行所有依赖的 effect
  dep.forEach(effect => effect());
}

// 创建响应式对象
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver);
      // 收集依赖
      track(target, key);
      return result;
    },
    set(target, key, value, receiver) {
      const oldValue = target[key];
      const result = Reflect.set(target, key, value, receiver);
      // 只有值变化时才触发更新
      if (oldValue !== value) {
        trigger(target, key);
      }
      return result;
    }
  });
}

2. effect 函数实现

// 当前激活的 effect
let activeEffect = null;

// 创建副作用函数
function effect(fn) {
  const effectFn = () => {
    try {
      // 设置当前激活的 effect
      activeEffect = effectFn;
      // 清理之前的依赖
      cleanup(effectFn);
      // 执行副作用函数
      return fn();
    } finally {
      // 重置当前激活的 effect
      activeEffect = null;
    }
  };
  
  // 存储依赖集合
  effectFn.deps = [];
  // 立即执行一次
  effectFn();
  
  return effectFn;
}

// 清理依赖
function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const dep = effectFn.deps[i];
    dep.delete(effectFn);
  }
  effectFn.deps.length = 0;
}

三、使用示例

1. 简单响应式示例

// 创建响应式对象
const state = reactive({
  count: 0,
  message: 'Hello Vue3'
});

// 创建副作用函数
effect(() => {
  console.log(`count is: ${state.count}`);
});

// 修改属性,触发更新
state.count = 1; // 输出: count is: 1
state.count = 2; // 输出: count is: 2

2. 嵌套响应式对象

const state = reactive({
  user: {
    name: 'John',
    age: 30
  },
  list: [1, 2, 3]
});

effect(() => {
  console.log(`User: ${state.user.name}, Age: ${state.user.age}`);
});

// 修改嵌套属性
state.user.age = 31; // 输出: User: John, Age: 31

四、Vue3 响应式系统的优化

1. 与 Vue2 的对比优势

  • 深度响应式:默认递归监听所有属性
  • 支持新增/删除属性:Proxy 可以拦截 delete 操作
  • 支持 Map、Set 等数据结构:Object.defineProperty 仅支持对象属性
  • 更好的性能:不需要预先遍历所有属性

2. 性能优化

// 浅响应式,只监听第一层属性
function shallowReactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver);
      track(target, key);
      return result;
    },
    set(target, key, value, receiver) {
      const oldValue = target[key];
      const result = Reflect.set(target, key, value, receiver);
      if (oldValue !== value) {
        trigger(target, key);
      }
      return result;
    }
  });
}

// 只读响应式
function readonly(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver);
      return result;
    },
    set(target, key, value, receiver) {
      console.warn(`Cannot set readonly property: ${key}`);
      return true;
    }
  });
}

五、Vue3 响应式系统的高级特性

1. computed 实现

function computed(getter) {
  let value;
  let dirty = true;
  
  const effectFn = effect(() => {
    // 缓存计算结果
    value = getter();
    dirty = false;
  });
  
  return {
    get value() {
      if (dirty) {
        effectFn();
      }
      // 收集依赖
      track(this, 'value');
      return value;
    }
  };
}

2. watch 实现

function watch(source, cb) {
  let getter;
  
  // 处理不同类型的 source
  if (typeof source === 'function') {
    getter = source;
  } else {
    getter = () => traverse(source);
  }
  
  let oldValue, newValue;
  
  // 创建副作用函数
  const effectFn = effect(() => {
    newValue = getter();
    if (oldValue !== undefined) {
      cb(newValue, oldValue);
    }
    oldValue = newValue;
  });
}

// 递归遍历对象,触发所有属性的依赖收集
function traverse(value, seen = new Set()) {
  if (typeof value !== 'object' || value === null || seen.has(value)) {
    return;
  }
  
  seen.add(value);
  
  // 遍历对象的所有属性
  for (const key in value) {
    traverse(value[key], seen);
  }
  
  return value;
}

六、总结

Vue3 的响应式系统通过 Proxy 实现,相比 Vue2 有了显著的性能提升和功能增强。其核心原理是通过 Proxy 拦截对象的属性访问和修改,结合 effect 函数实现依赖收集和自动更新。

在实际应用中,理解响应式原理有助于编写更高效的组件,避免不必要的更新。同时,Vue3 提供的 shallowReactive、readonly、computed 和 watch 等高级特性,进一步增强了响应式系统的灵活性和实用性。

通过深入理解 Vue3 响应式原理,可以更好地利用其特性构建高性能、可维护的前端应用。

举报

相关推荐

0 条评论