reactive API

Vue 3 的 reactive API 是响应式系统的核心,基于 ES6 的 Proxy 实现,专为对象类型数据提供深层响应式能力。以下从原理、用法、局限性和进阶技巧全面解析:


⚙️ 一、核心原理与实现

  1. Proxy 代理机制
    reactive 将普通对象包装为 Proxy 代理对象,通过拦截属性的 读取(get)设置(set) 操作实现响应式:

    • 依赖收集(track):读取属性时,记录当前依赖的副作用函数(如视图更新函数)。
    • 触发更新(trigger):修改属性时,通知所有依赖执行更新。
    const state = reactive({ count: 0 });
    state.count++; // 触发 set 拦截器 → 更新视图
    
  2. 深层响应式
    嵌套对象的属性也会被自动转换为响应式:

    const state = reactive({ user: { name: 'Tom' } });
    state.user.name = 'Jerry'; // 深层属性修改仍触发更新
    
  3. 缓存优化
    通过 WeakMap 缓存原始对象与代理对象的映射,避免重复代理同一对象:

    const raw = {};
    const proxy1 = reactive(raw);
    const proxy2 = reactive(raw); // 返回 proxy1,避免重复创建
    

🛠️ 二、使用方式与特性

基础用法

import { reactive } from 'vue';
const state = reactive({
  count: 0,
  list: [1, 2, 3],
});
state.list.push(4); // 数组变更自动响应

自动解包 ref

reactive 对象的属性值是 ref 时,模板和脚本中访问会自动解包(无需 .value):

const num = ref(10);
const state = reactive({ num });
console.log(state.num); // 10(自动解包)

与 ref 的对比

特性reactiveref
适用类型对象/数组/集合类型基本类型 + 对象类型(需 .value
访问方式直接访问属性(state.count.value(脚本中)
重赋值❌ 整体替换丢失响应性✅ 支持(.value = newObj
解构响应性❌ 需配合 toRefs✅ 直接解构有效

何时用 reactive

  • 管理关联数据(如表单对象)
  • 无需整体替换的场景(如配置对象)
  • 需深层嵌套响应(如用户信息对象)

⚠️ 三、局限性及解决方案

  1. 仅支持对象类型
    原始类型(如 string/number)不可用 reactive,需用 ref

    const count = reactive(0); // 警告:参数必须是对象!
    
  2. 解构丢失响应性
    直接解构会失去响应连接:

    const state = reactive({ count: 0 });
    const { count } = state; // count 是普通值,非响应式!
    

    ✅ 解决方案

    import { toRefs } from 'vue';
    const state = reactive({ count: 0 });
    const { count } = toRefs(state); // count 转为 ref,保持响应
    
  3. 不可整体替换
    重赋值会破坏响应式:

    let state = reactive({ count: 0 });
    state = { count: 1 }; // ❌ 新对象无响应性
    

    ✅ 正确做法

    // 修改属性而非替换对象
    Object.assign(state, { count: 1 }); // ✅ 保持响应
    

🔧 四、进阶技巧与相关 API

1. 浅层响应式(shallowReactive

仅代理对象的第一层属性,深层属性不变为响应式,适合性能敏感场景:

import { shallowReactive } from 'vue';
const state = shallowReactive({ user: { name: 'Tom' } });
state.user.name = 'Jerry'; // ❌ 不触发更新(深层非响应)
state.user = { name: 'Jerry' }; // ✅ 触发更新(第一层响应)

2. 只读代理(readonly

禁止修改响应式对象:

const state = reactive({ count: 0 });
const readOnlyState = readonly(state);
readOnlyState.count = 1; // ❌ 控制台警告,修改失败

3. 原始对象获取(toRaw

绕过 Proxy 直接操作原始对象,避免触发更新:

const raw = toRaw(state); // 获取 state 的原始对象
raw.count = 100; // 修改不触发视图更新

💡 五、最佳实践

  1. 优先 const 声明:避免意外重赋值。
  2. 复杂对象扁平化:减少深层嵌套提升性能。
  3. 配合 computed/watch:实现派生数据与副作用逻辑:
    const double = computed(() => state.count * 2);
    watch(() => state.count, (newVal) => { /* ... */ });
    
  4. 避免与 ref 混用:对象属性推荐全用 reactive,基本类型用 ref

💎 总结

reactive 是 Vue 3 响应式系统的基石,通过 Proxy 代理深层响应ref 自动解包 简化对象类型数据管理。其局限可通过 toRefs 解构、shallowReactive 优化性能等方式规避。实践中,关联数据用 reactive,独立数据用 ref,结合计算属性与侦听器构建高效响应式逻辑。

评论