vue3中customRef的用法以及使用场景

devtools/2025/2/3 18:58:24/

1. 基本概念

customRef 是 Vue3 提供的用于创建自定义响应式引用的 API,允许显式地控制依赖追踪和触发响应。它返回一个带有 getset 函数的工厂函数来自定义 ref 的行为。

1.1 基本语法

import { customRef } from 'vue'function createCustomRef(value) {return customRef((track, trigger) => {return {get() {track() // 追踪依赖return value},set(newValue) {value = newValuetrigger() // 触发更新}}})
}

2. 常见使用场景

2.1 防抖 Ref

function useDebouncedRef(value, delay = 200) {let timeoutreturn customRef((track, trigger) => {return {get() {track()return value},set(newValue) {clearTimeout(timeout)timeout = setTimeout(() => {value = newValuetrigger()}, delay)}}})
}// 使用示例
const searchQuery = useDebouncedRef('', 500)// 在模板中使用
// <input v-model="searchQuery" />

2.2 节流 Ref

function useThrottledRef(value, delay = 200) {let lastTriggerTime = 0return customRef((track, trigger) => {return {get() {track()return value},set(newValue) {const now = Date.now()if (now - lastTriggerTime >= delay) {value = newValuelastTriggerTime = nowtrigger()}}}})
}// 使用示例
const scrollPosition = useThrottledRef(0, 100)

2.3 验证 Ref

function useValidatedRef(value, validator) {return customRef((track, trigger) => {return {get() {track()return value},set(newValue) {if (validator(newValue)) {value = newValuetrigger()} else {console.warn('Invalid value:', newValue)}}}})
}// 使用示例
const age = useValidatedRef(18, (value) => {return Number.isInteger(value) && value >= 0 && value <= 120
})

2.4 异步 Ref

function useAsyncRef(getter) {let value = nulllet isLoading = trueconst ref = customRef((track, trigger) => {// 初始加载数据getter().then(data => {value = dataisLoading = falsetrigger()})return {get() {track()return { value, isLoading }},set() {throw new Error('Async ref is readonly')}}})return ref
}// 使用示例
const userProfile = useAsyncRef(async () => {const response = await fetch('/api/user')return response.json()
})

3. 高级应用场景

3.1 持久化 Ref

function useLocalStorageRef(key, defaultValue) {const storedValue = JSON.parse(localStorage.getItem(key) || 'null')let value = storedValue !== null ? storedValue : defaultValuereturn customRef((track, trigger) => {return {get() {track()return value},set(newValue) {value = newValuelocalStorage.setItem(key, JSON.stringify(newValue))trigger()}}})
}// 使用示例
const theme = useLocalStorageRef('app-theme', 'light')

3.2 格式化 Ref

function useFormattedRef(value, formatter, parser) {return customRef((track, trigger) => {return {get() {track()return formatter(value)},set(newValue) {value = parser(newValue)trigger()}}})
}// 使用示例
const price = useFormattedRef(1000,(value) => `$${value.toFixed(2)}`,(value) => parseFloat(value.replace('$', ''))
)

4. 实际应用示例

4.1 表单输入处理

<template><div><input v-model="email" /><p>状态: {{ email.status }}</p><p>错误信息: {{ email.error }}</p></div>
</template><script setup>
function useValidatedEmailRef(initialValue = '') {let value = initialValuelet status = 'initial'let error = ''const validateEmail = (email) => {const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/return regex.test(email)}return customRef((track, trigger) => {return {get() {track()return {value,status,error}},set(newValue) {value = newValueif (!newValue) {status = 'initial'error = ''} else if (validateEmail(newValue)) {status = 'valid'error = ''} else {status = 'invalid'error = '请输入有效的邮箱地址'}trigger()}}})
}const email = useValidatedEmailRef()
</script>

4.2 搜索优化

<template><div><input v-model="searchQuery" /><div v-if="searchQuery.isLoading">加载中...</div><ul v-else><li v-for="result in searchQuery.results" :key="result.id">{{ result.title }}</li></ul></div>
</template><script setup>
function useSearchRef(initialValue = '') {let value = initialValuelet results = []let isLoading = falseconst performSearch = async (query) => {if (!query) {results = []return}isLoading = truetry {const response = await fetch(`/api/search?q=${query}`)results = await response.json()} catch (error) {console.error('Search failed:', error)results = []} finally {isLoading = false}}return customRef((track, trigger) => {return {get() {track()return {value,results,isLoading}},set(newValue) {value = newValueperformSearch(newValue).then(() => trigger())}}})
}const searchQuery = useSearchRef()
</script>

5. 最佳实践

5.1 性能优化

// 避免不必要的触发
function useOptimizedRef(value) {return customRef((track, trigger) => {return {get() {track()return value},set(newValue) {// 只在值真正改变时触发更新if (value !== newValue) {value = newValuetrigger()}}}})
}

5.2 错误处理

function useSafeRef(value, errorHandler = console.error) {return customRef((track, trigger) => {return {get() {try {track()return value} catch (error) {errorHandler(error)return null}},set(newValue) {try {value = newValuetrigger()} catch (error) {errorHandler(error)}}}})
}

6. 注意事项

  1. 避免过度使用
// ❌ 不要为简单的值使用 customRef
const simpleValue = customRef((track, trigger) => ({get() {track()return value},set(newValue) {value = newValuetrigger()}
}))// ✅ 使用普通的 ref
const simpleValue = ref(value)
  1. 保持响应性
// 确保在需要的时候调用 track 和 trigger
function useCustomRef(value) {return customRef((track, trigger) => ({get() {track() // 不要忘记 trackreturn value},set(newValue) {value = newValuetrigger() // 不要忘记 trigger}}))
}
  1. 内存管理
// 清理副作用
function useCustomRef(value) {let cleanup = nullreturn customRef((track, trigger) => ({get() {track()return value},set(newValue) {// 清理之前的副作用if (cleanup) {cleanup()}value = newValue// 设置新的副作用cleanup = setupSideEffect(value)trigger()}}))
}

http://www.ppmy.cn/devtools/155806.html

相关文章

Linux之内存管理前世今生(一)

一个程序&#xff08;如王者荣耀&#xff09;平常是存储在硬盘上的&#xff0c;运行时才把这个程序载入内存&#xff0c;CPU才能执行。 问题&#xff1a; 这个程序载入内存的哪个位置呢&#xff1f;载入内核所在的空间吗&#xff1f;系统直接挂了。 一、虚拟内存 1.1 内存分…

OSCP 渗透测试:网络抓包工具的使用指南

在 OSCP 考试和渗透测试中&#xff0c;网络数据分析是至关重要的技能。无论是嗅探明文密码、分析恶意流量&#xff0c;还是溯源攻击&#xff0c;抓包工具都是我们的得力助手。 本文将介绍 OSI 七层网络模型 及其在网络分析中的作用&#xff0c;并详细讲解 Wireshark 和 tcpdum…

【Pytest】生成html报告中,中文乱码问题解决方案

链接上一篇文章:https://blog.csdn.net/u013080870/article/details/145369926?spm1001.2014.3001.5502 中文乱码问题&#xff0c;python3&#xff0c;Python3.7后&#xff0c;还一个文件就是result.py 因为中文可以在内容中&#xff0c;也可能在文件名&#xff0c;类名&…

Git 常用命令汇总

# 推荐一个十分好用的git插件---->GitLens 其实很多命令操作完全界面化了&#xff0c;鼠标点点就可以实现但是命令是必要的&#xff0c;用多了你就知道了 Git 常用命令汇总 1. Git 基础操作 命令作用git init初始化本地仓库git clone <repo-url>克隆远程仓库到本地g…

论文阅读(十三):复杂表型关联的贝叶斯、基于系统的多层次分析:从解释到决策

1.论文链接&#xff1a;Bayesian, Systems-based, Multilevel Analysis of Associations for Complex Phenotypes: from Interpretation to Decision 摘要&#xff1a; 遗传关联研究&#xff08;GAS&#xff09;报告的结果相对稀缺&#xff0c;促使许多研究方向。尽管关联概念…

【HarmonyOS之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(二)

目录 1 -> HML语法 1.1 -> 页面结构 1.2 -> 数据绑定 1.3 -> 普通事件绑定 1.4 -> 冒泡事件绑定5 1.5 -> 捕获事件绑定5 1.6 -> 列表渲染 1.7 -> 条件渲染 1.8 -> 逻辑控制块 1.9 -> 模板引用 2 -> CSS语法 2.1 -> 尺寸单位 …

全面解析文件上传下载删除漏洞:风险与应对

在数字化转型的时代&#xff0c;文件上传、下载与删除功能已经成为各类应用程序的标准配置&#xff0c;从日常办公使用的协同平台&#xff0c;到云端存储服务&#xff0c;再到社交网络应用&#xff0c;这些功能在给用户带来便捷体验、显著提升工作效率的同时&#xff0c;也隐藏…

Electricity Market Optimization 探索系列(二)

​ 本文参考链接link 负荷持续时间曲线 (Load Duration Curve)&#xff0c;是根据实际的符合数据进行降序排序之后得到的一个曲线 这个曲线能够发现负荷在某个区间时&#xff0c;将会持续多长时间&#xff0c;有助于发电容量的规划 净负荷(net load) 是指预期负荷和预期可再生…