首页 / 资讯中心 / 文章详情

Vue与React的ref机制对比:响应式与非响应式差异

Vue与React的ref机制对比:响应式与非响应式差异 ★ FEATURED ARTICLE
1. Vue 和 React 的 ref 机制本质差异在 Vue 和 React 这两个主流前端框架中ref 都是用于直接访问 DOM 元素或组件实例的重要特性但它们的实现理念和使用方式存在根本性区别。Vue 的 ref 更像是框架响应式系统的一个扩展功能而 React 的 ref 则是为了突破其函数式编程模型限制而设计的逃生舱。Vue3 的 ref 实现基于 Proxy 的响应式系统。当我们使用ref()创建引用时实际上是在创建一个响应式对象这个对象通过.value属性来访问实际值。这种设计使得 ref 可以无缝融入 Vue 的响应式更新机制中。// Vue3 的 ref 使用示例 import { ref } from vue const myRef ref(null) // 创建 ref console.log(myRef.value) // 通过 .value 访问实际值相比之下React 的 ref 实现则更为底层。在函数组件中useRef创建的 ref 对象只是一个普通的 JavaScript 对象它的.current属性用于存储可变值。React 不会对 ref 的变化做出任何响应这是 React 函数式编程模型的直接体现。// React 的 useRef 使用示例 import { useRef } from react const myRef useRef(null) // 创建 ref console.log(myRef.current) // 通过 .current 访问实际值关键区别Vue 的 ref 是响应式的改变其值会触发组件更新React 的 ref 是非响应式的修改 ref 不会导致组件重新渲染。2. 模板中使用 ref 的语法对比2.1 Vue 的模板 ref 绑定Vue 提供了两种方式来使用 ref选项式 API 中的this.$refs组合式 API 中的ref()函数在模板中我们通过特殊的ref属性来标记需要引用的元素或组件template !-- 绑定到 DOM 元素 -- div refmyDivHello Vue/div !-- 绑定到组件实例 -- ChildComponent refchild / /template script export default { mounted() { console.log(this.$refs.myDiv) // 访问 DOM 元素 console.log(this.$refs.child) // 访问组件实例 } } /script在组合式 API 中我们需要显式声明 ref 并返回template div refmyDivHello Vue/div /template script setup import { ref, onMounted } from vue const myDiv ref(null) onMounted(() { console.log(myDiv.value) // 访问 DOM 元素 }) /script2.2 React 的 ref 绑定React 的 ref 绑定方式随着版本演进有所变化字符串 ref已废弃不推荐使用div refmyDivHello React/div回调 refdiv ref{(el) { this.myDiv el }}Hello React/divcreateRef (类组件)class MyComponent extends React.Component { constructor(props) { super(props) this.myRef React.createRef() } render() { return div ref{this.myRef}Hello React/div } }useRef (函数组件)function MyComponent() { const myRef useRef(null) return div ref{myRef}Hello React/div }实际开发中函数组件应使用useRef类组件可使用createRef。回调 ref 虽然灵活但性能较差字符串 ref 已被官方废弃。3. ref 的类型系统与 TypeScript 支持3.1 Vue 的 ref 类型推断Vue3 与 TypeScript 的集成非常紧密ref 的类型推断十分智能import { ref } from vue // 自动推断为 Refnumber const count ref(0) // 显式指定复杂类型 interface User { name: string age: number } const user refUser({ name: Alice, age: 25 })当 ref 用于模板引用时Vue 能自动推断出正确的 DOM 类型const el refHTMLDivElement | null(null)3.2 React 的 ref 类型处理React 的 ref 类型处理需要更多手动干预import { useRef } from react // 基础类型 const count useRefnumber(0) // DOM 引用 const divRef useRefHTMLDivElement(null)对于组件引用React 需要明确指定实例类型class ChildComponent extends React.Component { doSomething() { /* ... */ } } function ParentComponent() { const childRef useRefChildComponent(null) return ChildComponent ref{childRef} / }Vue 的类型推断更加自动化特别是在模板引用方面React 需要更多手动类型声明特别是在引用自定义组件时。4. ref 的高级用法与性能考量4.1 Vue 的响应式 ref 技巧解构 ref 保持响应性import { ref, toRefs } from vue const state ref({ count: 0, name: Vue }) // 错误方式失去响应性 const { count, name } state.value // 正确方式使用 toRefs const { count, name } toRefs(state.value)动态 ref 绑定template div v-foritem in list :refsetItemRef {{ item }} /div /template script setup import { ref } from vue const list ref([1, 2, 3]) const itemRefs ref([]) const setItemRef el { if (el) { itemRefs.value.push(el) } } /script4.2 React 的 ref 性能优化避免在渲染函数中创建 ref// 错误做法每次渲染都创建新函数 div ref{(el) { this.myRef el }} / // 正确做法使用 useCallback 或 useRef const setRef useCallback((el) { if (el) { // 处理 ref } }, [])转发 ref (forwardRef)const FancyButton React.forwardRef((props, ref) ( button ref{ref} classNamefancy {props.children} /button )) // 父组件可以获取到 button 的 ref const ref useRef() FancyButton ref{ref}Click me!/FancyButton命令式句柄 (useImperativeHandle)const FancyInput forwardRef((props, ref) { const inputRef useRef() useImperativeHandle(ref, () ({ focus: () { inputRef.current.focus() } })) return input ref{inputRef} / })5. 常见问题与解决方案5.1 Vue 的 ref 常见坑生命周期问题import { ref, onMounted } from vue const el ref(null) // 错误setup 阶段 DOM 尚未挂载 console.log(el.value) // null onMounted(() { // 正确此时可以访问 DOM console.log(el.value) })数组 ref 的注意事项template div v-foritem in list :refsetItemRef {{ item }} /div /template script setup const itemRefs ref([]) // 必须检查 el 是否存在因为 v-for 更新时会先传 null 清理旧 ref const setItemRef el { if (el) { itemRefs.value.push(el) } } /script5.2 React 的 ref 常见问题条件渲染导致 ref 为 nullfunction MyComponent() { const ref useRef(null) return ( {condition div ref{ref}Content/div} button onClick{() { // 可能为 null console.log(ref.current) }}Log Ref/button / ) }函数组件不能直接接收 ref// 错误函数组件没有实例 const MyFunctionComponent () divHello/div const ref useRef() MyFunctionComponent ref{ref} / // 警告 // 正确使用 forwardRef const MyFunctionComponent forwardRef((props, ref) ( div ref{ref}Hello/div ))闭包陷阱function Timer() { const countRef useRef(0) useEffect(() { const timer setInterval(() { countRef.current // 可以正常工作 }, 1000) return () clearInterval(timer) }, []) // 显示的值不会更新因为 ref 变化不会触发渲染 return div{countRef.current}/div }6. 实际应用场景对比6.1 适合使用 Vue ref 的场景表单输入聚焦template input refinput v-modeltext / button clickfocusInputFocus/button /template script setup import { ref } from vue const input ref(null) const text ref() function focusInput() { input.value.focus() } /script组件方法调用template ChildComponent refchild / button clickcallChildMethodCall Method/button /template script setup import { ref } from vue const child ref(null) function callChildMethod() { child.value.doSomething() } /script6.2 适合使用 React ref 的场景动画控制function AnimatedBox() { const boxRef useRef() useEffect(() { const animation boxRef.current.animate( [{ transform: translateX(0) }, { transform: translateX(100px) }], { duration: 1000 } ) return () animation.cancel() }, []) return div ref{boxRef} style{{ width: 100, height: 100, background: red }} / }第三方库集成function ChartComponent() { const chartRef useRef() const [data, setData] useState(/* ... */) useEffect(() { const chart new ThirdPartyChartLibrary(chartRef.current) chart.render(data) return () chart.destroy() }, [data]) return div ref{chartRef} / }测量 DOM 元素function MeasureExample() { const divRef useRef() const [size, setSize] useState({ width: 0, height: 0 }) useLayoutEffect(() { if (divRef.current) { setSize({ width: divRef.current.offsetWidth, height: divRef.current.offsetHeight }) } }, []) return ( div ref{divRef} Size: {size.width} x {size.height} /div ) }7. 设计哲学差异解析Vue 的 ref 设计体现了其渐进式框架的理念与响应式系统深度集成提供多种使用方式适应不同开发者偏好模板中的使用方式简单直观自动处理大多数类型推断React 的 ref 设计反映了其函数式编程原则显式优于隐式必须手动访问 .current不可变性优先ref 变化不会触发渲染组合优于继承forwardRef 机制明确区分可变与不可变状态这种根本差异导致了两者在 ref 使用体验上的显著不同。Vue 开发者可以更自然地使用 ref而 React 开发者需要更明确地处理副作用和可变状态。
阅读完成 · 觉得有帮助?
咨询建站