在 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 的get和set行为。
- 特性: 它接收一个工厂函数,该函数有两个参数
track(用于追踪依赖)和trigger(用于触发更新),并返回一个包含get和set的对象。
举个栗子:防抖——推迟函数的执行,直到高频事件停止触发后的一小段时间,优化性能和防止资源浪费。
<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请求。
-
数据同步: 创建一个与
localStorage或sessionStorage自动同步的 ref,在set时自动写入存储。 -
错误处理/验证: 在
set操作中加入数据验证逻辑。 -
撤销/重做 (Undo/Redo): 在
set时维护一个历史状态栈。
5.总结与对比
| API | 响应性 | 性能 | 用途 |
|---|---|---|---|
ref | 深层 | 默认 | 最常用,适用于所有类型的数据 |
shallowRef | 浅层(.value) | 高 | 优化大型数据结构,集成第三方库 |
triggerRef | 工具 | 工具 | 手动触发shallowRef的更新 |
customRef | 自定义 | 自定义 | 实现防抖、节流、localstorage同步等高级逻辑 |
参考文章
小满zs 学习Vue3 第六章(认识Ref全家桶) xiaoman.blog.csdn.net/article/det…