掌握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 refinputRef typetext /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 refchildRef / /template script setup const childRef ref(null) // 调用子组件方法 childRef.value.someMethod() /script3. shallowRef性能优化的秘密武器shallowRef是ref的轻量版它只跟踪.value的变化不会深度转换嵌套对象。这在处理大型对象时能显著提升性能。import { shallowRef } from vue const bigData shallowRef({ // 包含大量数据的复杂对象 }) // 直接替换value会触发响应式更新 bigData.value newValue // 修改嵌套属性不会触发更新 bigData.value.nested.prop new value // 不会触发更新!我在开发一个数据可视化项目时遇到了性能问题。数据量很大但很少需要深度更新换成shallowRef后性能提升了约30%。如果需要强制更新shallowRef的值可以使用triggerRefimport { 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的过程中我踩过不少坑这里分享几个常见的忘记.value在script中修改ref值时容易忘记写.valueconst count ref(0) count // 错误应该是count.value解构失去响应性直接解构ref会失去响应性const state ref({ count: 0 }) const { count } state.value // count不是响应式的异步更新问题在同一个事件循环中多次修改refVue会合并更新const count ref(0) count.value count.value console.log(count.value) // 2但DOM可能只更新一次模板引用未挂载在onMounted之前访问模板引用会是nullconst inputRef ref(null) console.log(inputRef.value) // null因为组件还没挂载7. 性能优化技巧经过多个项目的实践我总结了一些ref性能优化的经验对于大型不可变数据使用shallowRef避免在渲染函数中创建新的ref合理使用computed减少不必要的ref更新对于频繁更新的ref考虑使用customRef实现节流在v-for中使用ref时考虑使用函数ref避免性能问题// 不好的做法 div v-foritem in list :refitemRefs/div // 更好的做法 div v-foritem in list :refel { if(el) itemRefs[item.id] el }/div8. TypeScript支持Vue3对TypeScript的支持非常完善ref也能很好地与TS配合。我们可以为ref指定明确的类型const count refnumber(0) // 明确指定为number类型 interface User { name: string age: number } const user refUser({ name: 张三, age: 20 })对于模板引用我们也可以指定具体的元素类型const inputRef refHTMLInputElement | null(null)在子组件引用时可以使用InstanceType来获取组件实例类型const childRef refInstanceTypetypeof 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-modeltext / /template在开发复杂组件时这种组合使用可以大大简化代码逻辑。