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

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()
    }
  }))
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

React 前端框架实战教程

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 引言 React 是由 Facebook 开发的前端 JavaScript 库&#xff0c;旨在构建高效、灵活的用户界面&#xff0c;尤其适用于单页应用…

MiniMax-01中Lightning Attention的由来(线性注意力进化史)

目录 引言原始注意力线性注意力因果模型存在的问题累加求和操作的限制Lightning AttentionLightning Attention-1Lightning Attention-2 备注 引言 MiniMax-01: Scaling Foundation Models with Lightning Attention表明自己是第一个将线性注意力应用到如此大规模的模型&#…

互联网医院成品|互联网医院软件源码

互联网医院系统带来的好处是显而易见的&#xff0c;其通过先进的互联网技术为医疗行业带来了巨大的变革。以下将从多个方面详细阐述其带来的益处。 一、便捷的医疗服务 互联网医院系统为患者提供了更为便捷的医疗服务。患者无需再亲自前往医院&#xff0c;只需通过电脑、手机等…

unity学习20:time相关基础 Time.time 和 Time.deltaTime

目录 1 unity里的几种基本时间 1.1 time 相关测试脚本 1.2 游戏开始到现在所用的时间 Time.time 1.3 时间缩放值 Time.timeScale 1.4 固定时间间隔 Time.fixedDeltaTime 1.5 两次响应时间之间的间隔&#xff1a;Time.deltaTime 1.6 对应测试代码 1.7 需要关注的2个基本…

Centos7系统php8编译安装ImageMagick/Imagick扩展教程整理

Centos7系统php8编译安装ImageMagick/Imagick扩展教程整理 安装php8安装ImageMagick1、下载ImageMagick2、解压并安装3、查看是否安装成功 安装imagick扩展包 安装php8 点我安装php8 安装ImageMagick 1、下载ImageMagick wget https://www.imagemagick.org/download/ImageMa…

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…

Linux——网络基础(1)

文章目录 目录 文章目录 前言 一、文件传输协议 应用层 传输层 网络层 数据链路层 数据接收与解封装 主机与网卡 数据传输过程示意 二、IP和MAC地址 定义与性质 地址格式 分配方式 作用范围 可见性与可获取性 生活例子 定义 用途 特点 联系 四、TCP和UDP协…

SpringBoot+Electron教务管理系统 附带详细运行指导视频

文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码1.查询课程表代码2.保存学生信息代码3.用户登录代码 一、项目演示 项目演示地址&#xff1a; 视频地址 二、项目介绍 项目描述&#xff1a;这是一个基于SpringBootElectron框架开发的教务管理系统。首先&#xff…

有限元分析学习——Anasys Workbanch第一阶段笔记(15)接触间隙处理与赫兹接触

目录 0 序言 1 接触的间隙处理 1.1 结果对比 1.2 处理方法 2 赫兹接触 0 序言 本章主要介绍间隙出现时的三种解决方法&#xff0c;齿轮点蚀/表面剥落涉及的赫兹接触的一些理论知识。 1 接触的间隙处理 在实际产品过程中&#xff0c;很多时候由于设计问题&#xff0c;原本…

go单元测试和基准测试

1、单元测试和基准测试 单元测试和基准测试代码开发中的重要环节&#xff0c;良好的单元测试和基准测试&#xff0c;能提升开发质量&#xff0c;对整体开发有非常重要的重要&#xff0c;下面介绍单元测试和基准测试的写法。 2、单元测试和基准测试写法 以排序基本排序算法&a…

自由窗口边框阴影描绘方案汇总-社群讨论学习

背景&#xff1a; 针对很多厂商的自由窗口都有内外阴影效果&#xff0c;针对这样一个需求其实不仔细看可能都看不出来&#xff0c;不过确实也是存在的这种阴影&#xff0c;这里我搞个明显一些的给大家看看&#xff1a; 针对这样一个需求&#xff0c;在我们vip学员群里进行了相…

应用层协议 HTTP 讲解实战:从0实现HTTP 服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; HTTP 协议 &#x1f98b; 认识 URL&#x1f98b; urlencode 和 urldecode 二&#xff1a;&#x1f525; HTTP 协议请求与响应格式 &#x1f98b; HTTP 请求…

深入剖析 Adam 优化器:原理、优势与应用

在深度学习领域&#xff0c;优化器的选择对模型的训练效率和性能起着决定性作用。Adam优化器作为一种自适应优化算法&#xff0c;凭借其根据历史梯度信息动态调整学习率的特性&#xff0c;备受研究者和工程师的青睐。它巧妙融合了RMSProp和Momentum两种优化算法的理念&#xff…

ppp综合实验

IP地址 r1 r2 r3 r4 hdlc封装 pap认证 r2 r3 chap认证 r2 r4 MGRE 主认证 [r1]int Tunnel 0/0/0 [r1-Tunnel0/0/0]ip add 192.168.4.1 24 [r1-Tunnel0/0/0]tunnel-protocol gre p2mp [r1-Tunnel0/0/0]source 12.1.1.1 [r1-Tunnel0/0/0]nhrp entry multicast dynamic [r1-Tu…

C++——list的了解和使用

目录 引言 forward_list与list 标准库中的list 一、list的常用接口 1.list的迭代器 2.list的初始化 3.list的容量操作 4.list的访问操作 5.list的修改操作 6.list的其他操作 二、list与vector的对比 结束语 引言 本篇博客要介绍的是STL中的list。 求点赞收藏评论…

Elasticsearch+kibana安装(简单易上手)

下载ES( Download Elasticsearch | Elastic ) 将ES安装包解压缩 解压后目录如下: 修改ES服务端口&#xff08;可以不修改&#xff09; 启动ES 记住这些内容 验证ES是否启动成功 下载kibana( Download Kibana Free | Get Started Now | Elastic ) 解压后的kibana目…

Ansible自动化运维实战--script、unarchive和shell模块(6/8)

文章目录 一、script模块1.1、功能1.2、常用参数1.3、举例 二、unarchive模块2.1、功能2.2、常用参数2.3、举例 三、shell模块3.1、功能3.2、常用参数3.3、举例 一、script模块 1.1、功能 Ansible 的 script 模块允许你在远程主机上运行本地的脚本文件&#xff0c;其提供了一…

Unity Epplus读取excel表并存入So文件举例

目录 此篇需要你有一定的阅读代码的能力&#xff0c;不然点开了也不知道在做什么 这是读表工具 So文件这么写 使用 此篇需要你有一定的阅读代码的能力&#xff0c;不然点开了也不知道在做什么 在此之前你需要知道epplus是干什么的&#xff0c;然后知道其基本api&#xff0…

Java Web-Cookie与Session

会话跟踪技术 会话跟踪技术是一种在 Web 应用程序中跟踪用户会话状态的机制&#xff0c;它允许服务器在多个请求之间识别和关联属于同一用户的请求&#xff0c;以便在整个会话过程中保持用户相关的信息。以下是几种常见的会话跟踪技术&#xff1a; Cookie 概念&#xff1a;Cook…

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

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