当前位置: 首页 > news >正文

掌握Vue3 第六章(Ref全家桶实战与避坑指南)

1. Ref基础:从入门到精通

Vue3的ref函数可以说是响应式编程的基石。我第一次接触ref时,也被它简洁而强大的特性所吸引。ref的核心作用就是将一个普通的JavaScript值转换为响应式对象,这个对象有一个.value属性指向原始值。

import { ref } from 'vue' // 基本用法 const count = ref(0) console.log(count.value) // 0 // 修改值 count.value++ console.log(count.value) // 1

在实际项目中,我经常用ref来处理表单输入。比如下面这个用户登录场景:

const username = ref('') const password = ref('') const handleLogin = () => { if(username.value && password.value) { // 登录逻辑 } }

这里有个新手容易踩的坑:在模板中使用ref时不需要.value,但在script中必须使用.value。我刚开始用Vue3时就经常忘记写.value,导致代码不工作。

ref对对象和数组的处理也很智能。它会递归地将所有嵌套属性都转为响应式:

const user = ref({ name: '张三', address: { city: '北京' } }) // 修改嵌套属性 user.value.address.city = '上海' // 这个修改也是响应式的

2. 模板引用:操作DOM的利器

模板引用是ref的另一个重要用途。通过给DOM元素添加ref属性,我们可以在组件挂载后直接操作DOM元素。这在集成第三方库或实现特定UI效果时特别有用。

<template> <input ref="inputRef" type="text"> </template> <script setup> import { ref, onMounted } from 'vue' const inputRef = ref(null) onMounted(() => { // 组件挂载后,inputRef.value就是真实的DOM元素 inputRef.value.focus() }) </script>

我在一个项目中需要实现图片懒加载,就是通过模板引用结合IntersectionObserver实现的:

const imgRef = ref(null) onMounted(() => { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if(entry.isIntersecting) { entry.target.src = entry.target.dataset.src observer.unobserve(entry.target) } }) }) observer.observe(imgRef.value) })

模板引用也可以用在子组件上,这时ref.value获取的是子组件的实例:

<template> <ChildComponent ref="childRef" /> </template> <script setup> const childRef = ref(null) // 调用子组件方法 childRef.value.someMethod() </script>

3. shallowRef:性能优化的秘密武器

shallowRef是ref的"轻量版",它只跟踪.value的变化,不会深度转换嵌套对象。这在处理大型对象时能显著提升性能。

import { shallowRef } from 'vue' const bigData = shallowRef({ // 包含大量数据的复杂对象 }) // 直接替换value会触发响应式更新 bigData.value = newValue // 修改嵌套属性不会触发更新 bigData.value.nested.prop = 'new value' // 不会触发更新!

我在开发一个数据可视化项目时,遇到了性能问题。数据量很大但很少需要深度更新,换成shallowRef后性能提升了约30%。

如果需要强制更新shallowRef的值,可以使用triggerRef:

import { shallowRef, triggerRef } from 'vue' const state = shallowRef({ count: 0 }) // 修改嵌套属性 state.value.count++ // 手动触发更新 triggerRef(state)

4. customRef:打造专属响应式逻辑

customRef允许我们自定义ref的行为,这在需要实现防抖、节流等高级功能时特别有用。

下面是一个防抖ref的实现:

import { customRef } from 'vue' function debouncedRef(value, delay = 500) { let timer return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timer) timer = setTimeout(() => { value = newValue trigger() }, delay) } } }) } // 使用示例 const searchText = debouncedRef('', 300)

我在开发搜索功能时就用到了这个自定义ref。用户连续输入时不会立即触发搜索,而是在停止输入300ms后才执行搜索请求,既提升了性能又改善了用户体验。

5. Ref与Reactive的抉择

很多开发者纠结该用ref还是reactive。根据我的经验,ref更适合处理基本类型和需要替换整个对象的情况,而reactive更适合处理不需要替换的复杂对象。

// 适合用ref的场景 const count = ref(0) const user = ref({ name: '张三' }) // 适合用reactive的场景 const form = reactive({ username: '', password: '' })

Vue官方推荐优先使用ref,因为它的行为更一致,而且在组合式函数中更容易传递。我在项目中也是主要使用ref,只有在处理紧密相关的多个属性时才会考虑reactive。

6. 实战中的常见陷阱

在使用ref的过程中,我踩过不少坑,这里分享几个常见的:

  1. 忘记.value:在script中修改ref值时容易忘记写.value
const count = ref(0) count++ // 错误!应该是count.value++
  1. 解构失去响应性:直接解构ref会失去响应性
const state = ref({ count: 0 }) const { count } = state.value // count不是响应式的
  1. 异步更新问题:在同一个事件循环中多次修改ref,Vue会合并更新
const count = ref(0) count.value++ count.value++ console.log(count.value) // 2,但DOM可能只更新一次
  1. 模板引用未挂载:在onMounted之前访问模板引用会是null
const inputRef = ref(null) console.log(inputRef.value) // null,因为组件还没挂载

7. 性能优化技巧

经过多个项目的实践,我总结了一些ref性能优化的经验:

  1. 对于大型不可变数据,使用shallowRef
  2. 避免在渲染函数中创建新的ref
  3. 合理使用computed减少不必要的ref更新
  4. 对于频繁更新的ref,考虑使用customRef实现节流
  5. 在v-for中使用ref时,考虑使用函数ref避免性能问题
// 不好的做法 <div v-for="item in list" :ref="itemRefs"></div> // 更好的做法 <div v-for="item in list" :ref="el => { if(el) itemRefs[item.id] = el }"></div>

8. TypeScript支持

Vue3对TypeScript的支持非常完善,ref也能很好地与TS配合。我们可以为ref指定明确的类型:

const count = ref<number>(0) // 明确指定为number类型 interface User { name: string age: number } const user = ref<User>({ name: '张三', age: 20 })

对于模板引用,我们也可以指定具体的元素类型:

const inputRef = ref<HTMLInputElement | null>(null)

在子组件引用时,可以使用InstanceType来获取组件实例类型:

const childRef = ref<InstanceType<typeof ChildComponent> | null>(null)

9. 组合式函数中的Ref

在编写组合式函数时,ref是最常用的工具之一。它允许我们在函数之间共享响应式状态:

// useCounter.ts export function useCounter(initialValue = 0) { const count = ref(initialValue) const increment = () => count.value++ const decrement = () => count.value-- return { count, increment, decrement } } // 在组件中使用 const { count, increment } = useCounter()

我经常用这种模式来封装可复用的业务逻辑,比如表单验证、数据获取等。ref的这种特性让代码组织变得更加灵活。

10. 与其他特性的配合

ref可以很好地与Vue的其他特性配合使用。比如与watch一起使用:

const count = ref(0) watch(count, (newVal, oldVal) => { console.log(`count从${oldVal}变为${newVal}`) })

与v-model配合使用:

const text = ref('') <template> <input v-model="text" /> </template>

在开发复杂组件时,这种组合使用可以大大简化代码逻辑。

http://www.cnnetsun.cn/news/3388401.html

相关文章:

  • 小米智能音箱Pro体验:平衡音质与智能交互的家庭枢纽
  • 记录:hbuildx 无线真机调试,公司网络无法连接问题
  • Kilo Code接入GLM-4.6本地补全实战指南
  • DRA75x引脚配置实战:从GMAC、GPIO到eMMC的硬件设计与软件避坑指南
  • Weknora AI知识库问答系统检索指标测试方法
  • IP与IK防护:厂家必须知道的可靠性真相
  • C++高精度算法实现:从原理到工程实践,手把手构建大整数运算库
  • STM32 UART串口中断接收与状态机解析实战(一种高效稳定的解析范式)
  • TI MSP430F676x1A三相电能计量SoC:架构、低功耗设计与开发实战
  • 告别setup.py:使用pyproject.toml打造现代Python包
  • 基于STM8与DS18B20的LCD1602四线驱动温度监测系统实现
  • DLP3010芯片组应用实战:从光学设计到热管理的系统级避坑指南
  • 生产管理5M1E:人、机、料、法、环、测到底该怎么理解?
  • Unity动画事件实战:帧级精准触发游戏音效与特效
  • 实战指南|心理学实验样本量计算:从理论到实践
  • Unity Shader Graph纹理数组:性能优化与实战应用指南
  • 在YOLOv11中引入Transformer自注意力:是锦上添花还是负优化?
  • 攻克英语期刊写作:从Results到Discussion的实战解析与避坑指南
  • 从性能瓶颈到无缝融合:使用Boost.Python构建C++与Python的混合计算引擎
  • 人机识别对抗——鼠标轨迹模拟:贝塞尔曲线、噪声注入、速度变化与验证通过
  • 实战演练:利用MATLAB构建(7,4)汉明码的完整通信链路仿真
  • PyBind11 2.12 零拷贝数据交换:C++与Python高性能混合编程实践
  • 【强化学习】MCTS 中的 UCB 变体:从理论到实战的探索与利用平衡术
  • 基于STM32单片机的USB有线键盘无线键盘数字键盘设计/DIY-T154
  • 从几何变换到数据降维:特征值与特征向量的核心应用解析
  • 智能车浅谈——从PID到模型预测:方向控制算法演进
  • FPD-Link III解串器DS90UB662-Q1:多路视频同步与CSI-2协议实战解析
  • 基于stm32单片机超声波测距 倒车雷达 防撞报警 语音播报设计系统312(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • Shell脚本:巧用cat与EOF实现动态配置文件的自动化生成
  • GPT-5.6医疗健康能力评估:AI在生命科学与临床研究的突破应用