Vue 3 自定义 ref 实战:用 customRef 封装防抖 ref 与异步 loading ref

test12026-10-210 次阅读

customRef 是什么

ref 的诞生是自动的:读写就触发依赖收集与更新。但当你想自己掌控“什么时候收集依赖、什么时候触发更新”时,就要用 customRef。它接收一个工厂函数,工厂拿到 track(收集依赖)与 trigger(触发更新)两个钩子,你决定 get 时是否 track、set 时是否立刻 trigger。

封装一个防抖 ref

输入框实时搜索时,我们不希望每次击键都更新绑定值。下面的 useDebouncedRef 在 set 时延迟一段时间才真正落值并通知视图。

import { customRef } from 'vue';

export function useDebouncedRef<T>(value: T, delay = 300) {
  let timeout: ReturnType<typeof setTimeout>;
  return customRef<T>((track, trigger) => ({
    get() {
      track();
      return value;
    },
    set(newValue: T) {
      clearTimeout(timeout);
      timeout = setTimeout(() => {
        value = newValue;
        trigger();
      }, delay);
    },
  }));
}

// 使用
const keyword = useDebouncedRef('', 400);
// 模板里 v-model="keyword",停手 400ms 后才更新,可拿去发请求

封装一个异步 loading ref

把“加载中”和“取值”绑在一起,调用方只关心 value 与 isLoading,不必自己维护两个 ref。

import { customRef, ref } from 'vue';

export function useAsyncRef<T>() {
  const state = ref<T | null>(null);
  let loading = false;
  const value = customRef<T>((track, trigger) => ({
    get() {
      track();
      return state.value as T;
    },
    set(v: T) {
      state.value = v;
      trigger();
    },
  }));
  const load = async (promise: Promise<T>) => {
    loading = true;
    state.value = (await promise) as T;
    loading = false;
    trigger();
  };
  return { value, load, isLoading: () => loading };
}

注意事项

  • get 里一定要调用 track,否则依赖它的组件不会重新渲染。
  • set 里不调用 trigger,视图就不会更新;延迟触发时记得在定时器里调。
  • customRef 适合“读写时机需要定制”的场景,普通状态用普通 ref 即可,不要过度设计。
T

test1

文章作者

customRef 是什么 ref 的诞生是自动的:读写就触发依赖收集与更新。但当你想自己掌控“什么时候收集依赖、什么时...

分类
技术
发布时间
2026-10-21
字数
约 1517 字
阅读
0 次