📖 概述
customRef()
是 Vue 3 中用于创建自定义响应式引用的组合式 API。它允许开发者完全控制响应式数据的读取和写入行为,为复杂的响应式逻辑提供了强大的灵活性。
🎯 基本概念
什么是 customRef?
customRef()
是一个工厂函数,它接受一个工厂函数作为参数,返回一个自定义的响应式引用。通过自定义get
和set
函数,可以实现复杂的响应式逻辑。
核心特性
特性 | 描述 |
---|---|
完全控制 | 自定义 getter 和 setter 逻辑 |
延迟计算 | 支持懒加载和缓存机制 |
副作用处理 | 精确控制依赖收集和触发更新 |
类型安全 | 完整的 TypeScript 支持 |
🔧 函数签名
function customRef<T>(
factory: (
track: () => void,
trigger: () => void
) => {
get: () => T;
set: (value: T) => void;
}
): Ref<T>;
📋 参数说明
参数 | 类型 | 描述 |
---|---|---|
track | () => void | 依赖收集函数,在 getter 中调用 |
trigger | () => void | 触发更新函数,在 setter 中调用 |
🎯 使用场景
1️⃣ 防抖输入框
创建带有防抖功能的输入框,避免频繁触发更新。
2️⃣ 异步数据加载
实现懒加载和缓存机制的响应式数据。
3️⃣ 数据验证和转换
在数据写入时进行验证和格式转换。
💻 代码示例
🚀 基础用法
import { customRef } from "vue";
// 创建一个简单的自定义 ref
const count = customRef((track, trigger) => {
let value = 0;
return {
get() {
track(); // 收集依赖
return value;
},
set(newValue) {
value = newValue;
trigger(); // 触发更新
},
};
});
// 使用
console.log(count.value); // 0
count.value = 10;
console.log(count.value); // 10
⏱️ 防抖输入框
import { customRef } from "vue";
function useDebouncedRef(initialValue, delay = 300) {
return customRef((track, trigger) => {
let value = initialValue;
let timeoutId = null;
return {
get() {
track();
return value;
},
set(newValue) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
value = newValue;
trigger();
}, delay);
},
};
});
}
// 在组件中使用
const searchQuery = useDebouncedRef("", 500);
🔄 异步数据加载
import { customRef } from "vue";
function useAsyncRef(fetcher) {
return customRef((track, trigger) => {
let value = null;
let loading = false;
let error = null;
const load = async () => {
if (loading) return;
loading = true;
error = null;
trigger();
try {
value = await fetcher();
} catch (err) {
error = err;
} finally {
loading = false;
trigger();
}
};
return {
get() {
track();
if (value === null && !loading) {
load();
}
return { value, loading, error };
},
set(newValue) {
value = newValue;
trigger();
},
};
});
}
// 使用示例
const userData = useAsyncRef(() =>
fetch("/api/user").then((res) => res.json())
);
✅ 数据验证
import { customRef } from "vue";
function useValidatedRef(initialValue, validator) {
return customRef((track, trigger) => {
let value = initialValue;
let error = null;
return {
get() {
track();
return { value, error };
},
set(newValue) {
try {
const validationResult = validator(newValue);
if (validationResult === true) {
value = newValue;
error = null;
} else {
error = validationResult;
}
} catch (err) {
error = err.message;
}
trigger();
},
};
});
}
// 使用示例
const age = useValidatedRef(18, (value) => {
if (value < 0) return "年龄不能为负数";
if (value > 150) return "年龄不能超过150岁";
return true;
});
🎨 在模板中使用
<template>
<div>
<!-- 防抖输入框 -->
<input v-model="searchQuery" placeholder="搜索..." />
<p>搜索内容: {{ searchQuery }}</p>
<!-- 异步数据 -->
<div v-if="userData.loading">加载中...</div>
<div v-else-if="userData.error">错误: {{ userData.error }}</div>
<div v-else>{{ userData.value }}</div>
<!-- 数据验证 -->
<input v-model="age.value" type="number" />
<p v-if="age.error" style="color: red;">{{ age.error }}</p>
</div>
</template>
<script setup>
import { useDebouncedRef, useAsyncRef, useValidatedRef } from "./composables";
const searchQuery = useDebouncedRef("", 500);
const userData = useAsyncRef(() =>
fetch("/api/user").then((res) => res.json())
);
const age = useValidatedRef(18, (value) => {
if (value < 0) return "年龄不能为负数";
return true;
});
</script>
⚠️ 注意事项
🔢 依赖收集和触发
- ✅ 在
get()
函数中必须调用track()
- ✅ 在
set()
函数中必须调用trigger()
- ❌ 忘记调用会导致响应式失效
🕐 异步操作
- ⚠️ 在
set()
中进行异步操作时要小心 - 🔄 考虑使用
nextTick()
确保 DOM 更新
🧹 内存泄漏
- 🗑️ 及时清理定时器和事件监听器
- 🔄 在组件卸载时清理资源
🎯 最佳实践
1️⃣ 封装为组合式函数
将复杂的 customRef 逻辑封装为可复用的组合式函数。
2️⃣ 提供合理的默认值
为 customRef 提供合理的初始值,避免 undefined 状态。
3️⃣ 错误处理
在异步操作和验证逻辑中添加适当的错误处理。
4️⃣ 性能优化
避免在 getter 中进行昂贵的计算,考虑使用缓存机制。
❓ 常见问题
Q: customRef 和 computed 有什么区别?
A: customRef 提供完全的控制权,而 computed 是基于依赖的派生值。customRef 适合需要自定义 get/set 逻辑的场景。
Q: 可以在 customRef 中使用其他响应式数据吗?
A: 可以,但需要确保正确调用 track() 来收集依赖。
Q: customRef 是否支持深层响应式?
A: 默认不支持,需要手动处理嵌套对象的响应式。
📝 总结
customRef()
是 Vue 3 中实现复杂响应式逻辑的强大工具。它提供了完全的控制权,适用于防抖、本地存储同步、异步数据加载等场景。通过合理使用track()
和trigger()
函数,可以创建高效且灵活的响应式数据。在开发中,建议将复杂的 customRef 逻辑封装为组合式函数,以提高代码的可复用性和可维护性。
原文链接:https://code.ifrontend.net/archives/1067,转载请注明出处。
评论0