Loading

vue3 中响应式API有哪些?

  • 核心响应式 API:创建响应式数据的基础。
  • 计算属性与侦听器:派生状态和副作用处理。

  • Ref 相关工具:用于操作和转换 ref

  • 浅层响应式 API:用于性能优化,只处理浅层响应性。

  • 原始值操作 API:与响应式系统交互的低级工具。

  • 响应式工具函数:用于检查和判断响应式状态。

1. 核心响应式 API

这是创建响应式数据最基础的 API。

reactive()

  • 作用:创建一个深度响应式的对象或数组的代理。

  • 特点

    • 深度转换:对象的所有嵌套属性都会被递归地转为响应式。

    • 仅用于对象类型:不能用于原始值(stringnumberboolean等)。

    • 解构丢失响应性:直接对 reactive 对象进行 ES6 解构,其属性会失去响应性。需要使用 toRefs 来解决。

  • 示例

    import { reactive } from 'vue';
    
    const state = reactive({
      count: 0,
      nested: {
        value: 'hello' // 这个属性也是响应式的
      },
      list: [1, 2, 3]
    });
    
    // 所有操作都是响应式的
    state.count++;
    state.nested.value = 'world'; // 触发更新
    state.list.push(4); // 触发更新

readonly()

  • 作用:接受一个对象(响应式或普通)或 ref,并返回一个原始对象的只读代理

  • 特点

    • 深度只读:任何尝试设置操作的都会失败并在开发模式下产生警告,包括嵌套属性。

    • 与源属性“链接”:如果源对象是响应式的,只读代理会同步变化,只是你不能直接修改它。

  • 示例

    import { reactive, readonly } from 'vue';
    
    const original = reactive({ count: 0 });
    const copy = readonly(original);
    
    original.count++; // 成功,copy.count 也会变为 1
    
    copy.count++; // 失败,并会有警告: "Set operation on key 'count' failed: target is readonly."

ref()

  • 作用:接受一个内部值并返回一个响应式且可变的 ref 对象ref 对象只有一个 .value property,指向该内部值。

  • 特点

    • 可用于任何类型:不像 reactiveref 可以包装任何值,包括原始值和对象。

    • .value 访问:在 JS 中读取或修改都需要通过 .value,在模板中会自动“解包”,无需 .value

    • 对象处理:如果 .value 是一个对象,会用 reactive() 深度转换其值。

  • 示例

    import { ref } from 'vue';
    
    const count = ref(0); // 原始值
    const objectRef = ref({ name: 'Vue' }); // 对象
    
    console.log(count.value); // 0
    count.value++; // 修改
    
    console.log(objectRef.value.name); // 'Vue',objectRef.value 是一个 reactive 对象
    objectRef.value.name = 'Vue 3'; // 修改
    <!-- 在模板中自动解包 -->
    <template>
      <div>{{ count }}</div> <!-- 无需 .value -->
    </template>

2. 计算属性与侦听器

computed()

  • 作用:创建一个依赖于其他响应式状态的计算属性

  • 特点

    • 缓存:计算结果会被缓存,只有依赖的响应式值发生变化时才会重新计算,性能高效。

    • 两种形式:接受一个 getter 函数(只读)或一个带有 get 和 set 函数的对象(可写)。

  • 示例

    import { ref, computed } from 'vue';
    
    const count = ref(0);
    
    // 1. 只读计算属性
    const doubleCount = computed(() => count.value * 2);
    console.log(doubleCount.value); // 0
    
    // 2. 可写计算属性
    const writableComputed = computed({
      get() {
        return count.value + 10;
      },
      set(newValue) {
        count.value = newValue - 10;
      }
    });
    writableComputed.value = 100;
    console.log(count.value); // 90

watch()

  • 作用:显式地侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。

  • 特点

    • 惰性:不会立即执行,只有在源发生变化时执行。

    • 精确控制:可以获取变化前的旧值和变化后的新值。

    • 多种数据源:可以侦听 refreactive 对象的属性、getter 函数或以上组成的数组。

  • 示例

    import { ref, reactive, watch } from 'vue';
    
    const count = ref(0);
    const state = reactive({ name: 'Alice' });
    
    // 侦听一个 ref
    watch(count, (newVal, oldVal) => {
      console.log(`count changed from ${oldVal} to ${newVal}`);
    });
    
    // 侦听一个 getter 函数(用于 reactive 的某个属性)
    watch(() => state.name, (newName, oldName) => {
      console.log(`Name changed from ${oldName} to ${newName}`);
    });
    
    // 侦听多个源
    watch([count, () => state.name], ([newCount, newName], [oldCount, oldName]) => {
      console.log('Multiple sources changed');
    });

watchEffect()

  • 作用:立即执行传入的函数,同时自动追踪其依赖,并在其依赖变更时重新运行该函数。

  • 特点

    • 自动依赖收集:无需手动指定侦听源,函数体内用到的响应式属性都会被自动收集为依赖。

    • 立即执行:会立即运行一次以收集依赖。

    • 更简洁:适用于依赖众多且逻辑紧密的场景。

  • 示例

    import { ref, watchEffect } from 'vue';
    
    const count = ref(0);
    
    const stop = watchEffect(() => {
      // 自动追踪 count.value
      console.log(`Count is now: ${count.value}`);
    });
    // 立即打印: "Count is now: 0"
    
    count.value++; // 重新执行,打印: "Count is now: 1"
    
    // 停止侦听器
    stop();

3. Ref 相关工具函数

toRef()

  • 作用:为源响应式对象上的某个属性创建一个 ref。这个 ref 会与其源属性保持同步

  • 适用场景:当你想要将 props 的某个属性传递给一个组合式函数时,确保即使该属性不存在,ref 也会保持响应式。

  • 示例

    import { reactive, toRef } from 'vue';
    
    const state = reactive({ foo: 1 });
    const fooRef = toRef(state, 'foo');
    
    // 修改 ref 会更新源
    fooRef.value++;
    console.log(state.foo); // 2
    
    // 修改源也会更新 ref
    state.foo++;
    console.log(fooRef.value); // 3

toRefs()

  • 作用:将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref

  • 适用场景:在从组合式函数返回 reactive 对象时,或在解构 props 时,保持响应性。

  • 示例

    import { reactive, toRefs } from 'vue';
    
    function useFeature() {
      const state = reactive({ x: 0, y: 0 });
      // ...逻辑
      return toRefs(state); // 返回 { x: Ref<number>, y: Ref<number> }
    }
    
    // 在组件中可以安全解构!
    const { x, y } = useFeature();
    // x 和 y 现在是 refs,模板中可直接使用

isRef()

  • 作用:检查一个值是否为一个 ref 对象。

  • 示例

    import { ref, isRef } from 'vue';
    
    const count = ref(0);
    console.log(isRef(count)); // true
    console.log(isRef(0)); // false

unref()

  • 作用:这是一个语法糖:val = isRef(val) ? val.value : val

  • 适用场景:在可能接收到 ref 或普通值的函数中非常有用。

  • 示例

    function useValue(maybeRef) {
      const value = unref(maybeRef); // 如果是 ref,取 .value,否则直接返回值
      // ...使用 value
    }

4. 浅层响应式 API (性能优化)

这些 API 只创建浅层响应式对象,忽略嵌套属性的响应式转换,在某些场景下可以提升性能。

shallowRef()

  • 作用:创建一个 ref,只跟踪其 .value 本身的变化,不关心 .value 内部的属性。

  • 与 ref 区别ref 会深度转换对象,shallowRef 不会。

  • 适用场景:存储大型数据结构(如复杂的嵌套对象)或不需要深度监听变化的外部第三方类实例。

  • 示例

    import { shallowRef, triggerRef } from 'vue';
    
    const bigObject = shallowRef({ deep: { nested: { value: 1 } } });
    
    // 这不会触发更新!
    bigObject.value.deep.nested.value = 2;
    
    // 这会触发更新,因为替换了整个 `.value`
    bigObject.value = { new: 'object' };
    
    // 强制触发更新(慎用)
    triggerRef(bigObject);

shallowReactive()

  • 作用:创建一个响应式代理,只跟踪其自身属性的响应性(第一层属性),忽略所有深层属性的响应性。

  • 与 reactive 区别reactive 是深度的,shallowReactive 是浅层的。

  • 示例

    import { shallowReactive } from 'vue';
    
    const state = shallowReactive({
      rootLevel: 'reactive',
      nested: { deepData: 'not reactive' }
    });
    
    state.rootLevel = 'new'; // 响应式,触发更新
    state.nested.deepData = 'new'; // 非响应式,不触发更新!

shallowReadonly()

  • 作用:类似于 readonly(),但只将根级别属性设置为只读,深层属性仍然可以被修改(尽管不是响应式的)。

  • 示例

    import { shallowReadonly } from 'vue';
    
    const state = shallowReadonly({
      root: 'readonly',
      nested: { value: 'writable' }
    });
    
    state.root = 'new'; // 失败,警告
    state.nested.value = 'new'; // 成功!但不会触发响应式更新

5. 原始值操作 API (高级)

toRaw()

  • 作用:返回由 reactive()readonly()shallowReactive() 或 shallowReadonly() 创建的代理对应的原始对象

  • 适用场景:用于临时读取数据而不会引起代理访问/跟踪开销,或是用于修改数据而不触发变更(谨慎使用!)。

  • 示例

    import { reactive, toRaw } from 'vue';
    
    const foo = {};
    const reactiveFoo = reactive(foo);
    
    console.log(toRaw(reactiveFoo) === foo); // true

markRaw()

  • 作用:标记一个对象,使其永远不会被转换为代理。返回对象本身。

  • 适用场景:当你有一个复杂的第三方类实例,或者一个不可变的数据结构,并且确信它们不需要响应性时,用于性能优化。

  • 示例

    import { reactive, markRaw } from 'vue';
    
    const complexObject = markRaw({
      // ...大型复杂数据,或第三方实例
    });
    
    const state = reactive({
      // 即使嵌套在 reactive 中,complexObject 也不会被转为代理
      data: complexObject
    });

6. 响应式工具函数

isReactive()

  • 作用:检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。

    isReactive(reactive({})); // true

isReadonly()

  • 作用:检查一个对象是否是由 readonly() 或 shallowReadonly() 创建的代理。

    isReadonly(readonly(reactive({}))); // true

isProxy()

  • 作用:检查一个对象是否是由 reactive()readonly()shallowReactive() 或 shallowReadonly() 创建的代理。

    isProxy(reactive({})); // true
    isProxy(readonly({})); // true

总结与选择指南

你的需求 推荐使用的 API
创建一个基本类型的响应式变量 ref
创建一个复杂对象/数组的响应式变量 reactive
基于其他状态创建一个派生值 computed
侦听响应式状态的变化并执行副作用 watch / watchEffect
解构 reactive 对象或 props 并保持响应性 toRefs
将 props 的某个属性安全传递给组合式函数 toRef
需要一个大型对象的响应式,但性能是关键 shallowRef / shallowReactive
需要创建一个不允许修改的响应式对象 readonly / shallowReadonly
需要跳过代理,直接操作或读取原始对象 toRaw
确保一个对象永远不会被转为响应式代理 markRaw

posted @ 2025-09-12 19:58  Carvers  阅读(15)  评论(0)    收藏  举报