Vue3-Ref

58 阅读3分钟

在 Vue 3 中,ref 家族的 API 是组合式 API (Composition API) 的核心部分,用于创建响应式数据。它们各自有不同的特性和使用场景,尤其是在处理性能优化和复杂逻辑时。

1.ref

ref 是最常用的响应式 API。它接收一个内部值,并返回一个响应式的、可变的 ref 对象。该对象只包含一个 .value 属性,指向这个内部值。

  • 特性: ref 是“深层”响应式的。如果 .value 是一个对象或数组,ref 会在内部自动使用 reactive() 将其转换为一个深层响应式的代理对象。
<template>
  <!-- 模板里可以直接使用变量 -->
  <div>
    {{ message }}
    <br />
    <button @click="changeMessage">改变message</button>
  </div>
</template>
<script setup lang="ts">
import { ref, shallowRef, triggerRef, customRef } from "vue";
let message = ref<string | number>("Loading...");

const changeMessage = () => {
  //被ref包裹的变量,用.value赋值
  message.value = 123;
};
</script>
<style scoped></style>

2.shallowRef

shallowRef 创建一个“浅层”的 ref。与 ref 不同,它只追踪 .value 属性本身的更改(即赋值操作),而不会使其内部的值(如果是对象或数组)具有深层响应性。

  • 特性: 只有 .value替换操作是响应式的。对 .value 内部属性的修改不会触发视图更新。
<template>
  <div>
    {{ obj.name }}
    <br />
    <button @click="changeObj">改变Obj</button>
  </div>
</template>
<script setup lang="ts">
import { ref, shallowRef, triggerRef, customRef } from "vue";
let obj = shallowRef({ name: "YaeZed", age: 25 });
const changeObj = () => {
  obj.value.name = "YaeZed2";
  //   console.log(obj.value.name); //输出YaeZed2,但模板里并没有更新
  //   obj.value = { name: "YaeZed2", age: 23 };
  //   console.log(obj.value); // 输出{name: "YaeZed2", age: 23},模板里更新了
  // 3.1 ref和shalloRef不能同时用,因为ref会更新页面,导致页面重新渲染,从而会影响shallowRef也更新
  //   message.value = "ref修改了";
  // 4.triggerRef()方法可以强制更新模板
  // triggerRef(obj);
};
</script>
<style scoped></style>
使用场景
  • 性能优化: 当你有一个非常庞大、层级很深的对象,并且你不关心其内部属性的变化,只关心整个对象是否被替换时。这可以避免 Vue 深度遍历和代理该对象的开销。
  • 集成第三方库: 当你希望在 Vue 组件中持有一个来自外部库(如 3D 渲染器、图表库)的实例时。这些实例通常很复杂且不需要被 Vue 深度代理,你只需要在替换整个实例时更新视图。

3.triggerRef

triggerRef 是一个辅助函数,用于手动触发shallowRef 关联的副作用(例如视图更新)。

  • 特性: 它“强制”一个 shallowRef 重新运行其所有依赖项。

4.customRef

customRef 是一个强大的“工厂函数”,用于创建自定义的 ref。它允许你完全控制 ref 的 getset 行为。

  • 特性: 它接收一个工厂函数,该函数有两个参数 track(用于追踪依赖)和 trigger(用于触发更新),并返回一个包含 getset 的对象。

举个栗子:防抖——推迟函数的执行,直到高频事件停止触发后的一小段时间,优化性能防止资源浪费

<template>
  <div>
    防抖:{{ name }}
    <br />
    <input type="text" v-model="name" />
  </div>
</template>
<script setup lang="ts">
import { ref, shallowRef, triggerRef, customRef } from "vue";
const MyRef = <T>(value: T) => {
  let timer: any;
  return customRef((track, trigger) => {
    return {
      get() {
        track();
        return value;
      },
      set(newValue: T) {
        clearTimeout(timer);
        timer = setTimeout(() => {
          // 设置定时器,防止频繁触发set
          console.log("触发了set");
          value = newValue;
          trigger();
        }, 1000);
      },
    };
  });
};
const name = MyRef<string>("YaeZed");
</script>
<style scoped></style>
使用场景
  • 防抖 (Debounce) / 节流 (Throttle): 如上例所示,非常适合用于搜索框输入,避免在用户每次按键时都触发更新或API请求。

  • 数据同步: 创建一个与 localStoragesessionStorage 自动同步的 ref,在 set 时自动写入存储。

  • 错误处理/验证:set 操作中加入数据验证逻辑。

  • 撤销/重做 (Undo/Redo):set 时维护一个历史状态栈。

5.总结与对比

API响应性性能用途
ref深层默认最常用,适用于所有类型的数据
shallowRef浅层(.value)优化大型数据结构,集成第三方库
triggerRef工具工具手动触发shallowRef的更新
customRef自定义自定义实现防抖、节流、localstorage同步等高级逻辑

参考文章

小满zs 学习Vue3 第六章(认识Ref全家桶) xiaoman.blog.csdn.net/article/det…