Vue3响应式原理 之 Proxy详解

Vue 3 使用 Proxy 重构了其响应式系统(Reactivity System),这是 Vue 3 最重要的改进之一。通过 Proxy,Vue 3 实现了更高效、更灵活的依赖追踪和更新触发机制。

1. 响应式系统的核心目标

Vue 的响应式系统需要解决两个核心问题:

  1. 依赖收集:自动追踪数据属性被哪些组件、计算属性或侦听器(effect)使用。

  2. 触发更新:当数据变化时,自动通知所有依赖它的地方进行更新。

Vue 2 使用 Object.defineProperty 实现响应式,但它存在以下问题:

  • 无法检测属性的添加或删除(需要 Vue.set/Vue.delete)。

  • 数组的某些操作(如通过索引修改元素、修改 length)无法触发更新。

  • 性能问题:递归遍历对象所有属性并转换为 getter/setter

Vue 3 使用 Proxy 解决了这些问题。

2. Proxy 在 Vue 3 中的具体应用

(1) 创建响应式对象

Vue 3 通过 reactive() 函数将普通对象转换为响应式对象:

import { reactive } from 'vue';

const obj = reactive({ count: 0 });

底层实现中,reactive() 内部使用 Proxy 代理目标对象。

(2) 依赖收集(Track)

当访问响应式对象的属性时,Proxy 的 get 陷阱会触发依赖收集:

const handlers = {
  get(target, key, receiver) {
    // 1. 触发依赖收集
    track(target, key);
    // 2. 返回属性值(如果是对象,递归代理)
    return Reflect.get(target, key, receiver);
  }
};
  • track(target, key):将当前正在运行的 effect(如组件的渲染函数)记录到依赖列表中。

(3) 触发更新(Trigger)

当修改响应式对象的属性时,Proxy 的 set 陷阱会触发更新:

const handlers = {
  set(target, key, value, receiver) {
    // 1. 设置属性值
    const result = Reflect.set(target, key, value, receiver);
    // 2. 触发依赖更新
    trigger(target, key);
    return result;
  }
};
  • trigger(target, key):通知所有依赖此属性的 effect 重新执行(如重新渲染组件)。

(4) 递归代理嵌套对象

Proxy 可以自动处理嵌套对象的响应式:

const obj = reactive({
  nested: { a: 1 } // 嵌套对象也会被代理
});

// 访问 obj.nested.a 会触发依赖收集
// 修改 obj.nested.a 会触发更新
  • Vue 3 在 get 陷阱中,如果发现属性值是对象,会递归调用 reactive() 将其转换为响应式对象。

(5) 支持动态属性和数组

Proxy 天然支持动态属性和数组操作:

const arr = reactive([1, 2, 3]);
arr.push(4); // 触发更新(Vue 2 中需要特殊处理)

const obj = reactive({});
obj.newProp = 42; // 触发更新(Vue 2 中需要 Vue.set)

3. 对比 Vue 2 的改进

特性Vue 2 (Object.defineProperty)Vue 3 (Proxy)
动态属性不支持(需 Vue.set支持
数组操作需重写数组方法(如 push直接支持
性能初始化时递归遍历所有属性按需代理(惰性转换)
嵌套对象需要递归初始化按需递归代理
删除属性 (delete)需要 Vue.delete直接支持

4. 核心代码实现(简化版)

以下是 Vue 3 响应式系统的简化实现:

// 依赖收集和触发逻辑
const targetMap = new WeakMap(); // 存储所有依赖关系

function track(target, key) {
  if (activeEffect) {
    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()));
    }
    dep.add(activeEffect);
  }
}

function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (depsMap) {
    const dep = depsMap.get(key);
    if (dep) {
      dep.forEach(effect => effect());
    }
  }
}

// 响应式代理
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      track(target, key);
      const res = Reflect.get(target, key, receiver);
      if (typeof res === 'object' && res !== null) {
        return reactive(res); // 递归代理嵌套对象
      }
      return res;
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      trigger(target, key);
      return result;
    }
  });
}

// 当前正在运行的 effect
let activeEffect = null;

function effect(fn) {
  activeEffect = fn;
  fn();
  activeEffect = null;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值