查看: 8729|回复: 3

Vue3 customRef防抖验证与localStorage同步

[复制链接]
发表于 2026-9-2 12:00:00 | 显示全部楼层 |阅读模式
customRef是Vue 3提供的高级响应式API,它允许开发者创建自定义ref对象,并完全控制依赖追踪和触发更新的时机。与普通ref相比,customRef在读取和赋值时可以插入自定义逻辑,因此适合防抖、验证、异步获取、本地存储同步等场景。

首先看customRef的基本结构。customRef接收一个工厂函数,工厂函数接收track和trigger两个参数,并返回包含get和set的对象。track在get中调用,用于收集依赖;trigger在set中调用,用于通知Vue值已变化。若get中缺少track,值变化时视图不会更新;若set中缺少trigger,赋值后不会触发重新渲染。

下面是一个最基础的自定义ref:
  1. import { customRef } from 'vue'
  2. const myCustomRef = customRef((track, trigger) => {
  3.   let value = 'initial value'
  4.   return {
  5.     get() {
  6.       track()
  7.       return value
  8.     },
  9.     set(newValue) {
  10.       value = newValue
  11.       trigger()
  12.     }
  13.   }
  14. })
复制代码

为什么需要customRef?因为普通ref赋值后立即触发视图更新,而业务中可能需要干预这个过程,比如输入防抖、节流、异步处理、数据验证、外部状态同步等。

场景一:防抖Ref。这是最经典用例,常用于搜索框。每次设置值时清除之前的定时器,延迟delay毫秒后再真正更新值和触发更新:
  1. import { customRef } from 'vue'
  2. function useDebouncedRef(initialValue, delay = 500) {
  3.   let timeoutId
  4.   let value = initialValue
  5.   return customRef((track, trigger) => {
  6.     return {
  7.       get() {
  8.         track()
  9.         return value
  10.       },
  11.       set(newValue) {
  12.         clearTimeout(timeoutId)
  13.         timeoutId = setTimeout(() => {
  14.           value = newValue
  15.           trigger()
  16.         }, delay)
  17.       }
  18.     }
  19.   })
  20. }
复制代码

组件里这样使用:const searchText = useDebouncedRef('', 500),再把 input 的 v-model 指向 searchText,停止输入500ms后才会更新视图。

场景二:带验证的Ref。在set中通过validator函数校验,合法才更新value,非法则把错误消息保存到error并触发更新:
  1. import { customRef } from 'vue'
  2. function useValidatedRef(initialValue, validator) {
  3.   let value = initialValue
  4.   let error = null
  5.   return customRef((track, trigger) => {
  6.     return {
  7.       get() {
  8.         track()
  9.         return { value, error }
  10.       },
  11.       set(newValue) {
  12.         const isValid = validator(newValue)
  13.         if (isValid === true) {
  14.           value = newValue
  15.           error = null
  16.         } else {
  17.           error = isValid
  18.         }
  19.         trigger()
  20.       }
  21.     }
  22.   })
  23. }
复制代码

使用时传入校验函数,例如校验年龄在0-120之间,非法时返回错误消息字符串。

场景三:LocalStorage同步Ref。读取初始值时优先从localStorage获取,set时同步写入localStorage,实现响应式数据与本地存储双向绑定:
  1. import { customRef } from 'vue'
  2. function useLocalStorageRef(key, initialValue) {
  3.   const storedValue = localStorage.getItem(key)
  4.   let value = storedValue ? JSON.parse(storedValue) : initialValue
  5.   return customRef((track, trigger) => {
  6.     return {
  7.       get() {
  8.         track()
  9.         return value
  10.       },
  11.       set(newValue) {
  12.         value = newValue
  13.         localStorage.setItem(key, JSON.stringify(newValue))
  14.         trigger()
  15.       }
  16.     }
  17.   })
  18. }
复制代码

比如const theme = useLocalStorageRef('theme', 'light'),修改theme.value时会自动同步到localStorage。

场景四:异步Ref。可以在set中触发API请求,并用loading和error状态辅助展示,请求完成后更新值:
  1. import { customRef } from 'vue'
  2. function useAsyncRef(asyncFetcher, initialValue = null) {
  3.   let value = initialValue
  4.   let loading = false
  5.   let error = null
  6.   return customRef((track, trigger) => {
  7.     return {
  8.       get() {
  9.         track()
  10.         return { value, loading, error }
  11.       },
  12.       async set(params) {
  13.         loading = true
  14.         error = null
  15.         trigger()
  16.         try {
  17.           const result = await asyncFetcher(params)
  18.           value = result
  19.         } catch (e) {
  20.           error = e
  21.         } finally {
  22.           loading = false
  23.           trigger()
  24.         }
  25.       }
  26.     }
  27.   })
  28. }
复制代码

customRef与普通ref的对比:普通ref依赖追踪自动、触发更新自动、灵活性低,适用常规状态管理;customRef需要手动调用track和trigger,可在get/set中插入任意逻辑,适合防抖、节流、异步、验证、外部同步等,但代码复杂度更高。

使用customRef的注意事项:第一,get中必须调用track,set中必须调用trigger,否则依赖收集或更新通知会失效。第二,避免在get或set中直接访问其他响应式对象的.value并立即触发更新,防止循环依赖。第三,内部状态通常用闭包保存,不要直接暴露value变量。第四,customRef控制力强但复杂度也高,普通ref、computed或watch能满足需求时优先使用它们。第五,customRef返回的对象与普通ref一样可以直接用于v-model绑定,所以很适合做防抖输入框等表单场景。

总结:customRef打破了默认响应式行为的限制,开发者通过track和trigger精确控制数据读取与更新时机,可以实现防抖、节流、异步处理、数据验证等功能,提升应用性能与用户体验。实际开发中建议将常用逻辑封装成Composable函数(Hooks),便于复用。
回复

使用道具 举报

发表于 2026-9-2 19:00:00 | 显示全部楼层

Re: Vue3 customRef防抖验证与localStorage同步

楼主的帖子很清晰,把 customRef 的几个典型场景都覆盖到了,特别是防抖和 localStorage 同步这两个例子很实用,代码也简洁易懂。 补充一点小建议:自定义 ref 里如果 set 里做了异步操作或者延迟更新,最好考虑组件卸载时清理定时器或者忽略过期请求,避免内存泄漏或者状态错乱。另外 localStorage 同步的时候注意一下 JSON.parse 可能抛异常,可以做个 try/catch 或者默认值兜底。 整体来说这贴对刚接触 customRef 的人帮助很大,期待楼主后续还能补充更多进阶玩法。
回复 支持 反对

使用道具 举报

发表于 2026-9-2 19:00:00 | 显示全部楼层

Re: Vue3 customRef防抖验证与localStorage同步

楼主这篇讲得挺清楚的,customRef 的四个场景都很典型。尤其防抖那个,直接解决搜索框频繁触发更新的痛点,代码也简洁,拿过来就能用。 不过我在想,验证那个例子里 get 返回的是对象 `{ value, error }`,那在模板里用起来可能要稍微处理一下,比如 `validated.value` 和 `validated.error`,如果用到 v-model 上可能还得拆开绑定。不知道楼主实际用的时候有没有封装成 computed 或者再包一层? 另外 localStorage 那个用例,如果是在 SSR 或者组件初始化阶段,`localStorage` 不存在会报错,感觉可以用 `typeof window !== 'undefined'` 判断一下,或者用 try/catch 兜底可能更稳。 总之这个 API 确实很灵活,谢谢楼主分享,期待更多实战写法。
回复 支持 反对

使用道具 举报

发表于 2026-9-2 19:05:00 | 显示全部楼层

Re: Vue3 customRef防抖验证与localStorage同步

这个帖子把customRef的典型场景梳理得很清楚,尤其是防抖和localStorage同步这两个例子非常实用。我平时写搜索框时都是自己手写定时器,看了这个封装思路后清爽很多。另外那个局部保存error状态再通过get返回的设计也挺巧妙,等于直接把校验状态也变成响应式的一部分。如果能补充一下在setup里使用这些自定义ref时的类型定义方式,或者顺带提一下track和trigger在异步场景下的调用时机注意事项,应该会更完整。不过总体来讲作为入门到进阶的过渡讲解已经挺到位了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-12 13:18 , Processed in 0.020650 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部