当我删掉1000行代码重写时,才明白ref不只是获取DOM这么简单。
一个错误的ref使用方式,让我们团队的项目延期了两个月。
如果你还认为Vue的ref和React的ref只是语法不同,那你可能正在挖一个巨坑。
我是一个踩过无数坑的前端老兵,从Vue 2.x时代一路摸爬滚打到现在。三年前,我带着"反正都是获取DOM元素"的天真想法,在一个大型项目中混用Vue和React的ref思维,结果差点把整个团队拖下水。今天,我要用血泪教训告诉你,Vue和React中的ref究竟有什么本质差异,以及如何在实战中避开那些致命陷阱。
从一个真实的血泪教训说起
2022年初,我们接到一个混合技术栈的项目——主应用使用Vue 3,但需要嵌入几个React组件。当时我信心满满地想:“ref不就是获取DOM吗?有多难?”
结果在处理一个复杂的表单联动功能时,我发现Vue的ref和React的ref在行为上完全不是一回事。Vue的ref是响应式的,而React的ref是可变的引用对象。这个看似微小的差异,导致了一系列连锁反应:
// Vue 3中的ref - 我以为很简单
const inputRef = ref(null)
const focusInput = () => {
inputRef.value.focus() // 必须通过.value访问
}
// React中的ref - 看起来差不多
const inputRef = useRef(null)
const focusInput = () => {
inputRef.current.focus() // 通过.current访问
}
当时我天真地以为,不就是.value
和.current
的区别吗?直到我在组件通信中栽了大跟头。
Vue ref的响应式魔法:双刃剑的威力
Vue的ref最大特点是响应式。这意味着当ref的值发生变化时,所有依赖它的地方都会自动更新。听起来很美好,但这也是最容易踩坑的地方。
// Vue 3 - 响应式ref的威力展示
<template>
<div>
<input ref="inputRef" v-model="inputValue" />
<p>输入长度:{
{
inputLength }}</p>
<button @click="focusAndMeasure">聚焦并测量</button>
</div>
</template>
<script setup>
import {
ref, computed, nextTick } from 'vue'
const inputRef = ref(null)
const inputValue = ref('')
// 响应式计算属性,自动更新
const inputLength = computed(() => inputValue.value.length)
const focusAndMeasure = async () => {
inputRef.value.focus()
// 等待DOM更新完成
await nextTick()
// 获取实际DOM尺寸
const rect = inputRef.value.getBoundingClientRect()
console.log(`输入框宽度:${
rect.width}px`)
}
</script>
这里有个关键点:Vue的ref在模板中可以直接使用(不需要.value
),但在script中必须通过.value
访问。这个不一致性曾经让我在调试时浪费了整个下午。
真实案例:在一个动态表单项目中,我需要根据用户输入动态调整表单布局。Vue的响应式ref让这个需求变得异常简单,但也带来了性能隐患。
// 性能陷阱:过度的响应式更新
const formRefs = ref([])
const formData = ref([])
// 错误做法:每次输入都触发大量计算
watch(formData, (newData) => {
newData.forEach((item, index) => {
if (formRefs.value[index]) {
// 这里会触发大量DOM操作
formRefs.value[index].updateValidation()
}
})
}, {
deep: true })
// 正确做法:使用防抖优化
import {
debounce } from 'lodash-es'
const debouncedUpdate = debounce(