Vue3 customRef 使用指南:学会自定义响应式引用

更新日期: 2025-10-12阅读: 30标签: 响应式

vue 3 的 customRef 是一个很有用的功能,它让我们可以创建自己想要的响应式数据。通过它,我们能够完全控制数据的读取和写入方式。这篇文章会详细介绍 customRef 的用法,帮助你理解如何在实际项目中使用它。


什么是 customRef?

customRef 是 Vue 3 提供的一个特殊函数,它可以创建一个自定义的响应式引用。与普通的 ref 不同,customRef 允许我们决定数据被读取和修改时会发生什么。

简单来说,customRef 就像是一个包装盒,你可以在盒子里设置自己的规则,决定别人如何查看和修改盒子里的东西。


customRef 的基本结构

customRef 接受一个函数作为参数,这个函数会收到两个重要的方法:track 和 trigger。

  • track:当数据被读取时调用,告诉 Vue 需要追踪这个数据的变化

  • trigger:当数据被修改时调用,通知 Vue 需要更新界面

这个函数需要返回一个包含 get 和 set 方法的对象:

import { customRef } from "vue";

const myRef = customRef((track, trigger) => {
  let value = "初始值";
  
  return {
    get() {
      track(); // 告诉 Vue 这个数据被读取了
      return value;
    },
    set(newValue) {
      value = newValue; // 更新数据
      trigger(); // 告诉 Vue 数据变了,需要更新界面
    }
  };
});


实际使用场景

1. 实现搜索框防抖

在搜索功能中,我们通常不希望用户每输入一个字符就立即搜索,这样会造成不必要的请求。使用 customRef 可以轻松实现防抖功能。

import { customRef } from "vue";

function useDebouncedRef(initialValue, delay = 500) {
  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 searchText = useDebouncedRef("", 500);

这样,只有当用户停止输入 500 毫秒后,搜索内容才会真正更新。

2. 处理异步数据

当我们需要从服务器获取数据时,customRef 可以帮助我们管理加载状态和错误信息。

import { customRef } from "vue";

function useAsyncRef(fetcher) {
  return customRef((track, trigger) => {
    let value = null;
    let loading = false;
    let error = null;
    
    const loadData = async () => {
      if (loading) return;
      
      loading = true;
      error = null;
      trigger(); // 通知界面加载状态变了
      
      try {
        value = await fetcher();
      } catch (err) {
        error = err.message;
      } finally {
        loading = false;
        trigger(); // 通知界面加载完成
      }
    };
    
    return {
      get() {
        track();
        // 如果还没有数据且不在加载中,就开始加载
        if (value === null && !loading) {
          loadData();
        }
        return { data: value, loading, error };
      },
      set(newValue) {
        value = newValue;
        trigger();
      }
    };
  });
}

// 使用示例
const userInfo = useAsyncRef(() => 
  fetch("/api/user/1").then(response => response.json())
);

3. 数据验证

我们可以在数据被设置时进行验证,确保数据的正确性。

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) {
        // 使用验证函数检查新值
        const result = validator(newValue);
        
        if (result === true) {
          value = newValue;
          error = null;
        } else {
          error = result; // 保存错误信息
        }
        
        trigger();
      }
    };
  });
}

// 使用示例
const age = useValidatedRef(18, (value) => {
  if (value < 0) return "年龄不能小于0";
  if (value > 150) return "年龄不能大于150";
  return true; // 表示验证通过
});


在 Vue 组件中使用

下面是一个完整的组件示例,展示了如何在页面中使用上面创建的各种 customRef:

<template>
  <div>
    <!-- 防抖搜索框 -->
    <div>
      <h3>搜索框(带防抖)</h3>
      <input v-model="searchText" placeholder="输入搜索内容..." />
      <p>搜索内容:{{ searchText }}</p>
    </div>
    
    <!-- 异步数据 -->
    <div>
      <h3>用户信息</h3>
      <div v-if="userInfo.loading">正在加载...</div>
      <div v-else-if="userInfo.error">加载失败:{{ userInfo.error }}</div>
      <div v-else>
        <p>用户名:{{ userInfo.data?.name }}</p>
        <p>邮箱:{{ userInfo.data?.email }}</p>
      </div>
    </div>
    
    <!-- 数据验证 -->
    <div>
      <h3>年龄验证</h3>
      <input v-model="age.value" type="number" />
      <p v-if="age.error" style="color: red;">{{ age.error }}</p>
      <p v-else>年龄:{{ age.value }}</p>
    </div>
  </div>
</template>

<script setup>
import { useDebouncedRef, useAsyncRef, useValidatedRef } from "./composables";

const searchText = useDebouncedRef("", 500);
const userInfo = useAsyncRef(() => 
  fetch("https://jsonplaceholder.typicode.com/users/1").then(res => res.json())
);
const age = useValidatedRef(18, (value) => {
  if (value < 0) return "年龄不能为负数";
  if (value > 150) return "年龄不能超过150岁";
  return true;
});
</script>


需要注意的地方

使用 customRef 时,有几个重要点需要记住:

  1. 不要忘记调用 track 和 trigger

    • 在 get 方法中必须调用 track()

    • 在 set 方法中必须调用 trigger()

    • 如果忘记调用,数据将不会正常响应变化

  2. 处理异步操作要小心

    • 在 set 方法中进行异步操作时,要确保在适当的时候调用 trigger()

    • 可以使用 Vue 的 nextTick 来确保界面正确更新

  3. 避免内存泄漏

    • 如果使用了定时器或事件监听器,记得在不需要时清理它们

    • 在组件销毁时,应该取消未完成的异步操作


常见问题解答

customRef 和 computed 有什么区别?

computed 用于基于其他数据计算出的值,而 customRef 用于需要完全控制读取和写入过程的情况。computed 的值是自动计算的,customRef 的值是由我们完全控制的。

可以在 customRef 中使用其他响应式数据吗?

可以,但需要确保在 get 方法中正确调用 track(),这样 Vue 才能追踪到依赖关系。

customRef 能处理嵌套对象吗?

默认情况下,customRef 不会深度追踪嵌套对象的变化。如果需要这个功能,你需要自己实现深度监听,或者结合使用 Vue 的 reactive。


总结

Vue 3 的 customRef 是一个强大而灵活的工具,它让我们能够创建符合特定需求的响应式数据。无论是实现防抖搜索、管理异步数据,还是添加数据验证,customRef 都能提供完美的解决方案。

通过将复杂的 customRef 逻辑封装成可重用的函数,我们可以让代码更加清晰和易于维护。虽然刚开始使用可能会觉得有些复杂,但一旦掌握,它将大大提升你处理复杂响应式逻辑的能力。

希望这篇文章能帮助你理解和应用 Vue 3 的 customRef 功能。在实际项目中多加练习,你会越来越熟练地使用这个强大的工具。

本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!

链接: https://fly63.com/article/detial/12998

移动端web app要使用rem实现自适应布局:font-size的响应式

rem是相对于根元素html,这样就意味着,我们只需要在根元素确定一个px字号,则可以来算出元素的宽高。

使用现代CSS的响应式版面

通过模块化缩放,使用传统属性和calc()来动态缩放你的字体大小.为字体大小使用百分比.给文本内容和媒体查询使用em,针对不同视口尺寸使用不同缩放值.视口越小,缩放比例越小,使用媒体查询或者media()函数基于视口来改变比例和基础字号

web响应式图片的5种实现

在目前的前端开发中,我们经常需要进行响应式的网站开发。本文着重介绍一下弹性图片,也就是响应式图片的解决方案:js或服务端、srcset 、sizes 、picture标签、svg图片

HTML5+CSS3响应式垂直时间轴,高端,大气

HTML5+CSS3响应式垂直时间轴,使用了HTML5标签<section>,时间轴中所有的内容包括标题、简介、时间和图像都放在.cd-timeline-block的DIV中,多个DIV形成一个序列,并把这些DIV放在<section>中。

实现响应式_CSS变量

CSS 变量是 CSS 引入的一个新特性,目前绝大多数浏览器已经支持了,它可以帮助我们用更少的代码写出同样多的样式,大大提高了工作效率,本篇文章将教你如何使用 CSS 变量(css variable)。CSS中原生的变量定义语法是:--*,变量使用语法是:var(--*),其中*表示变量名称

vue响应式原理及依赖收集

Vue通过设定对象属性的setter/getter方法来监听数据的变化,通过getter进行依赖收集,而每个setter方法就是一个观察者,在数据变更的时候通知订阅者更新视图。

vue响应式系统--observe、watcher、dep

Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的JavaScript 对象,而当你修改它们时,视图会进行更新,这使得状态管理非常简单直接,我们可以只关注数据本身

Responsive Web Design 响应式网页设计

常见的布局方案:固定布局:以像素作为页面的基本单位,不管设备屏幕及浏览器宽度,只设计一套尺寸;可切换的固定布局:同样以像素作为页面单位,参考主流设备尺寸

响应式布局的实现

响应式布局,即 Responsive design,在实现不同屏幕分辨率的终端上浏览网页的不同展示方式。通过响应式设计能使网站在手机和平板电脑上有更好的浏览阅读体验。响应式布局的关键不仅仅在于布局

深入响应式原理

说到响应式原理其实就是双向绑定的实现,说到 双向绑定 其实有两个操作,数据变化修改dom,input等文本框修改值的时候修改数据1. 数据变化 -> 修改dom;2. 通过表单修改value -> 修改数据

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!