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

news2025/1/29 13:54:27

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 = newValue
        trigger() // 触发更新
      }
    }
  })
}

2. 常见使用场景

2.1 防抖 Ref

function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      }
    }
  })
}

// 使用示例
const searchQuery = useDebouncedRef('', 500)

// 在模板中使用
// <input v-model="searchQuery" />

2.2 节流 Ref

function useThrottledRef(value, delay = 200) {
  let lastTriggerTime = 0
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        const now = Date.now()
        if (now - lastTriggerTime >= delay) {
          value = newValue
          lastTriggerTime = now
          trigger()
        }
      }
    }
  })
}

// 使用示例
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 = newValue
          trigger()
        } 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 = null
  let isLoading = true
  
  const ref = customRef((track, trigger) => {
    // 初始加载数据
    getter().then(data => {
      value = data
      isLoading = false
      trigger()
    })
    
    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 : defaultValue
  
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        value = newValue
        localStorage.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 = initialValue
  let 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 = newValue
        if (!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 = initialValue
  let results = []
  let isLoading = false
  
  const performSearch = async (query) => {
    if (!query) {
      results = []
      return
    }
    
    isLoading = true
    try {
      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 = newValue
        performSearch(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 = newValue
          trigger()
        }
      }
    }
  })
}

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 = newValue
          trigger()
        } catch (error) {
          errorHandler(error)
        }
      }
    }
  })
}

6. 注意事项

  1. 避免过度使用
// ❌ 不要为简单的值使用 customRef
const simpleValue = customRef((track, trigger) => ({
  get() {
    track()
    return value
  },
  set(newValue) {
    value = newValue
    trigger()
  }
}))

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2284203.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用

title: 深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用 date: 2025/1/26 updated: 2025/1/26 author: cmdragon excerpt: 在现代数据库管理系统中,索引技术是提高查询性能的重要手段。当数据量不断增长时,如何快速、有效地访问这些数据成为了数据库设计的核…

分布式系统学习:小结

关于分布式系统的学习就暂时告一段落了&#xff0c;下面整理了个思维导图&#xff0c;只涉及分布式的一些相关概念&#xff0c;需要的可自取。后面准备写下关于AI编程相关的技术文章&#xff0c;毕竟要紧跟时代的脚步嘛 思维导图xmind文件下载地址&#xff1a;https://download…

基于STM32的阿里云智能农业大棚

目录 前言&#xff1a; 项目效果演示&#xff1a; 一、简介 二、硬件需求准备 三、硬件框图 四、CubeMX配置 4.1、按键、蜂鸣器GPIO口配置 4.2、ADC输入配置 4.3、IIC——驱动OLED 4.4、DHT11温湿度读取 4.5、PWM配置——光照灯、水泵、风扇 4.6、串口——esp8266模…

WGCLOUD使用介绍 - 如何监控ActiveMQ和RabbitMQ

根据WGCLOUD官网的信息&#xff0c;目前没有针对ActiveMQ和RabbitMQ这两个组件专门做适配 不过可以使用WGCLOUD已经具备的通用监测模块&#xff1a;进程监测、端口监测或者日志监测、接口监测 来对这两个组件进行监控

Win11画图工具没了怎么重新安装

有些朋友想要简单地把图片另存为其他格式&#xff0c;或是进行一些编辑&#xff0c;但是发现自己的Win11系统里面没有画图工具&#xff0c;这可能是因为用户安装的是精简版的Win11系统&#xff0c;解决方法自然是重新安装一下画图工具&#xff0c;具体应该怎么做呢&#xff1f;…

“AI质量评估系统:智能守护,让品质无忧

嘿&#xff0c;各位小伙伴们&#xff01;今天咱们来聊聊一个在现代社会中越来越重要的角色——AI质量评估系统。你知道吗&#xff1f;在这个快速发展的时代&#xff0c;产品质量已经成为企业生存和发展的关键。而AI质量评估系统&#xff0c;就像是我们的智能守护神&#xff0c;…

Ubuntu 顶部状态栏 配置,gnu扩展程序

顶部状态栏 默认没有配置、隐藏的地方 安装使用Hide Top Bar 或Just Perfection等进行配置 1 安装 sudo apt install gnome-shell-extension-manager2 打开 安装的“扩展管理器” 3. 对顶部状态栏进行配置 使用Hide Top Bar 智能隐藏&#xff0c;或者使用Just Perfection 直…

FPGA 使用 CLOCK_LOW_FANOUT 约束

使用 CLOCK_LOW_FANOUT 约束 您可以使用 CLOCK_LOW_FANOUT 约束在单个时钟区域中包含时钟缓存负载。在由全局时钟缓存直接驱动的时钟网段 上对 CLOCK_LOW_FANOUT 进行设置&#xff0c;而且全局时钟缓存扇出必须低于 2000 个负载。 注释&#xff1a; 当与其他时钟约束配合…

RabbitMQ模块新增消息转换器

文章目录 1.目录结构2.代码1.pom.xml 排除logging2.RabbitMQConfig.java3.RabbitMQAutoConfiguration.java 1.目录结构 2.代码 1.pom.xml 排除logging <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/PO…

机器学习:支持向量机

支持向量机&#xff08;Support Vector Machine&#xff09;是一种二类分类模型&#xff0c;其基本模型定义为特征空间上的间隔最大的广义线性分类器&#xff0c;其学习策略便是间隔最大化&#xff0c;最终可转化为一个凸二次规划问题的求解。 假设两类数据可以被 H x : w T x…

Spring Boot(6)解决ruoyi框架连续快速发送post请求时,弹出“数据正在处理,请勿重复提交”提醒的问题

一、整个前言 在基于 Ruoyi 框架进行系统开发的过程中&#xff0c;我们常常会遇到各种有趣且具有挑战性的问题。今天&#xff0c;我们就来深入探讨一个在实际开发中较为常见的问题&#xff1a;当连续快速发送 Post 请求时&#xff0c;前端会弹出 “数据正在处理&#xff0c;请…

2023年版本IDEA复制项目并修改端口号和运行内存

2023年版本IDEA复制项目并修改端口号和运行内存 1 在idea中打开server面板&#xff0c;在server面板中选择需要复制的项目右键&#xff0c;点击弹出来的”复制配置…&#xff08;Edit Configuration…&#xff09;“。如果idea上没有server面板或者有server面板但没有springbo…

微信小程序怎么制作自己的小程序?手把手带你入门(适合新手小白观看)

对于初学者来说&#xff0c;制作一款微信小程序总感觉高大上&#xff0c;又害怕学不会。不过&#xff0c;今天我就用最简单、最有耐心的方式&#xff0c;一步一步给大家讲清楚!让你知道微信小程序的制作&#xff0c;居然可以这么轻松(希望你别吓跑啊!)。文中还加了实战经验&…

EventBus事件总线的使用以及优缺点

EventBus EventBus &#xff08;事件总线&#xff09;是一种组件通信方法&#xff0c;基于发布/订阅模式&#xff0c;能够实现业务代码解耦&#xff0c;提高开发效率 发布/订阅模式 发布/订阅模式是一种设计模式&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖…

vim如何设置自动缩进

:set autoindent 设置自动缩进 :set noautoindent 取消自动缩进 &#xff08;vim如何使设置自动缩进永久生效&#xff1a;vim如何使相关设置永久生效-CSDN博客&#xff09;

LongLoRA:高效扩展大语言模型上下文长度的微调方法

论文地址&#xff1a;https://arxiv.org/abs/2309.12307 github地址&#xff1a;https://github.com/dvlab-research/LongLoRA 1. 背景与挑战 大语言模型&#xff08;LLMs&#xff09;通常在预定义的上下文长度下进行训练&#xff0c;例如 LLaMA 的 2048 个 token 和 Llama2 的…

NoSQL使用详解

文章目录 NoSQL使用详解一、引言二、NoSQL数据库的基本概念三、NoSQL数据库的分类及使用场景1. 键值存储数据库示例代码&#xff08;Redis&#xff09;&#xff1a; 2. 文档存储数据库示例代码&#xff08;MongoDB&#xff09;&#xff1a; 3. 列存储数据库4. 图数据库 四、使用…

《FreqMamba: 从频率角度审视图像去雨问题》学习笔记

paper&#xff1a;FreqMamba: Viewing Mamba from a Frequency Perspective for Image Deraining GitHub&#xff1a;GitHub - aSleepyTree/FreqMamba 目录 摘要 1、介绍 2、相关工作 2.1 图像去雨 2.2 频率分析 2.3 状态空间模型 3、方法 3.1 动机 3.2 预备知识 3…

试用ChatGPT开发一个大语言模型聊天App

参考官方文档&#xff0c;安装android studio https://developer.android.com/studio/install?hlzh-cn 参考这个添加permission权限&#xff1a; https://blog.csdn.net/qingye_love/article/details/14452863 参考下面链接完成Android Studio 给项目添加 gradle 依赖 ht…

第30周:文献阅读

目录 摘要 Abstract 文献阅读 问题引入 方法论 堆叠集成模型 深度学习模型 创新点 堆叠模型 敏感性和不确定性分析 优化模型 实验研究 数据集 水质指数WQI的计算 模型的构建与训练 模型性能评估 敏感性和不确定性分析 结论 摘要 本文聚焦于利用深度学习算…