Vue3中customRef自定义ref过程

 更新时间:2025年09月29日 08:53:17   作者:Rysxt  
Vue3的customRef允许自定义响应式逻辑,通过get/set控制依赖追踪与更新触发,适用于防抖、验证、异步等场景,最佳实践包括封装函数、正确调用track/trigger、清理资源及优化性能

一、customRef 基础概念

​customRef​​ 是 Vue3 提供的高级响应式 API,用于创建​​自定义行为的 ref 对象​​。

与普通 ref(ref())的固定 getter/setter 逻辑不同,customRef 允许开发者​​完全控制依赖追踪和更新触发​​,适用于防抖、节流、异步更新、数据验证等复杂场景。

其核心原理是通过​​工厂函数​​返回包含 getset 方法的对象:

  • get():读取数据时触发,需调用 track() 告诉 Vue“这个值被依赖”;
  • set(newValue):修改数据时触发,需调用 trigger() 告诉 Vue“这个值更新了”。

二、customRef 基本用法

1. 创建简单 customRef

以下代码实现一个​​基础 customRef​​,功能与普通 ref 类型,但能更清晰地看到依赖追踪和更新触发的流程:

import { customRef } from 'vue';

// 工厂函数:接收初始值,返回 customRef 对象
function useBasicRef(initialValue) {
  let value = initialValue; // 闭包保存值

  return customRef((track, trigger) => {
    return {
      // 读取值时触发,调用 track() 收集依赖
      get() {
        track(); // 告诉 Vue 这个值被依赖(如模板中的 {{ value }})
        console.log('获取值:', value);
        return value;
      },
      // 修改值时触发,调用 trigger() 通知更新
      set(newValue) {
        value = newValue; // 更新值
        console.log('设置值:', newValue);
        trigger(); // 通知 Vue 重新渲染依赖该值的组件
      }
    };
  });
}

// 在组件中使用
const basicRef = useBasicRef('初始值');

2. 与普通 ref 的区别

特性普通 ref (ref())customRef (customRef())
​​逻辑控制​​固定 getter/setter(仅实现基本响应式)完全自定义(可插入验证、防抖等逻辑)
​​灵活性​​高(满足复杂场景需求)
​​适用场景​​基本响应式需求(如存储用户输入)需要特殊行为的场景(如延迟更新)

三、常见应用场景与实现

1. 防抖 ref(延迟更新)

​场景​​:用户输入时,避免频繁触发视图更新(如搜索框输入),仅在停止输入后更新。

import { customRef } from 'vue';

function useDebouncedRef(initialValue, delay = 500) {
  let timer = null; // 闭包保存定时器
  let value = initialValue;

  return customRef((track, trigger) => {
    return {
      get() {
        track(); // 收集依赖
        return value;
      },
      set(newValue) {
        clearTimeout(timer); // 清除之前的定时器
        timer = setTimeout(() => {
          value = newValue; // 延迟后更新值
          trigger(); // 触发更新
        }, delay);
      }
    };
  });
}

// 在组件中使用
const searchQuery = useDebouncedRef('', 500);
// 模板中:<input v-model="searchQuery" />

​效果​​:用户输入时,视图不会立即更新,仅在停止输入 500ms 后更新。

2. 数据验证 ref

​场景​​:输入数据时,实时验证合法性(如邮箱格式、年龄范围),若不合法则提示错误。

import { customRef } from 'vue';

function useValidatedRef(initialValue, validator) {
  let value = initialValue;
  let error = null;

  return customRef((track, trigger) => {
    return {
      get() {
        track(); // 收集依赖
        return { value, error }; // 返回值和错误信息
      },
      set(newValue) {
        const validationResult = validator(newValue); // 验证数据
        if (validationResult === true) {
          value = newValue; // 验证通过,更新值
          error = null;
        } else {
          error = validationResult; // 验证失败,保存错误信息
        }
        trigger(); // 触发更新(视图会显示错误信息)
      }
    };
  });
}

// 验证函数:检查年龄是否在 18-120 之间
const ageValidator = (value) => {
  if (typeof value !== 'number' || value < 18 || value > 120) {
    return '年龄必须在 18-120 之间';
  }
  return true;
};

// 在组件中使用
const age = useValidatedRef(20, ageValidator);
// 模板中:<input v-model.number="age.value" /><span v-if="age.error" style="color: red">{{ age.error }}</span>

​效果​​:输入非法年龄(如 10)时,会显示错误提示。

3. 异步 ref(异步更新)

​场景​​:从服务器获取数据后,更新 ref 的值(如用户信息加载)。

import { customRef } from 'vue';

function useAsyncRef(asyncFunction) {
  let value = null;
  let isLoading = true;

  return customRef((track, trigger) => {
    return {
      get() {
        track(); // 收集依赖
        return { value, isLoading }; // 返回值和加载状态
      },
      set(newValue) {
        // 触发异步操作(如获取用户信息)
        asyncFunction(newValue).then((data) => {
          value = data; // 更新值
          isLoading = false; // 结束加载
          trigger(); // 触发更新
        }).catch((error) => {
          console.error('异步操作失败:', error);
          isLoading = false; // 结束加载
          trigger(); // 触发更新
        });
      }
    };
  });
}

// 模拟异步函数:获取用户信息
const fetchUserInfo = (userId) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id: userId, name: '张三' });
    }, 1000);
  });
};

// 在组件中使用
const userInfo = useAsyncRef(fetchUserInfo);
// 模板中:<div v-if="userInfo.isLoading">加载中...</div><div v-else>{{ userInfo.value.name }}</div>

​效果​​:设置 userInfo.value 为 userId 后,会显示“加载中...”,1 秒后显示用户姓名。

四、best 实践

1. 封装为组合式函数

将 customRef 逻辑封装为独立的组合式函数(如 useDebouncedRefuseValidatedRef),提高代码复用性。避免在组件中重复编写 get/set 逻辑。

2. 正确处理依赖

get 方法中​​必须调用 track()​,否则 Vue 无法追踪依赖,导致视图不更新;在 set 方法中​​必须调用 trigger()​,否则视图不会重新渲染。

3. 避免内存泄漏

若 customRef 中使用了定时器、事件监听器等资源,需在组件卸载时清理(如 onUnmounted 钩子中清除定时器),防止内存泄漏。

4. 性能优化

  • 避免在 get 方法中执行昂贵计算(如复杂循环),可将结果缓存;
  • set 方法中,可通过比较新旧值(如 if (newValue !== value))避免不必要的更新。

通过以上教程,你可以掌握 Vue3 中 customRef 的基本用法和常见场景,灵活实现自定义响应式逻辑。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Vue3路由query参数实例详解

    Vue3路由query参数实例详解

    文章介绍了Vue3路由的query参数,包括其定义、使用方法以及如何在组件中接收和展示query参数,通过一个简单的实例,展示了如何在路由配置中添加query参数,并在子组件中通过计算属性获取和显示这些参数,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • vue项目打包后上传至GitHub并实现github-pages的预览

    vue项目打包后上传至GitHub并实现github-pages的预览

    这篇文章主要介绍了vue项目打包后上传至GitHub并实现github-pages的预览,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • vue中对接Graphql接口的实现示例

    vue中对接Graphql接口的实现示例

    本文主要介绍了vue中对接Graphql接口的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • Vue.js中computed的基本使用方法

    Vue.js中computed的基本使用方法

    Vue.js中,computed属性根据依赖进行缓存,只有依赖改变时才重新计算,这样有效提高性能,computed属性是响应式的,可以自动更新,并且默认是只读的,它与methods的主要区别在于计算属性具有缓存性,而方法每次调用都会执行,使用computed可以使模板更加简洁,提高应用性能
    2024-09-09
  • vue项目引入svg图标的完整步骤

    vue项目引入svg图标的完整步骤

    在实际的项目开发中,使用svg图标占用内存比图片更小,映入图片内存比较大,同时也适用于不同屏幕的尺寸,下面这篇文章主要给大家介绍了关于vue项目引入svg图标的完整步骤,需要的朋友可以参考下
    2022-10-10
  • vue路由守卫,限制前端页面访问权限的例子

    vue路由守卫,限制前端页面访问权限的例子

    今天小编就为大家分享一篇vue路由守卫,限制前端页面访问权限的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11
  • 使用vue-cli导入Element UI组件的方法

    使用vue-cli导入Element UI组件的方法

    这篇文章给大家介绍了使用vue-cli导入Element UI组件的方法,非常不错,具有一定的参考借鉴价值,需要的朋友一起看看吧
    2018-05-05
  • 一文带你快速了解Vue3服务端渲染SSR

    一文带你快速了解Vue3服务端渲染SSR

    在当今前端开发领域,首屏加载速度和 SEO 优化已成为衡量 Web 应用质量的重要指标,Vue3服务端渲染为解决这些问题提供了优雅的解决方案,下面小编就来和大家详细讲讲吧
    2025-05-05
  • Vue双向绑定原理深度剖析

    Vue双向绑定原理深度剖析

    Vue双向绑定是MVVM框架的核心特性,通过数据劫持Object.defineProperty或Proxy、依赖收集和模板编译三大模块实现,这篇文章主要介绍了Vue双向绑定原理的相关资料,需要的朋友可以参考下
    2026-03-03
  • vue如何集成raphael.js中国地图的方法示例

    vue如何集成raphael.js中国地图的方法示例

    最近的数据统计项目中要用到中国地图,也就是在地图上动态的显示某个时间段某个省份地区的统计数据,我们不需要flash,仅仅依靠raphael.js以及SVG图像就可以完成地图的交互操作。本文就给大家介绍了关于利用vue集成raphael.js中国地图的相关资料,需要的朋友可以参考下。
    2017-08-08

最新评论