React中使用useRef引用canvas时存储其参数的最优方法是什么?
频繁绘制canvas的场景下,useRef是三者中性能最优的方案,具体分析如下:
三类存储方案的优劣对比
1. 组件作用域var变量
这种写法存在不可控的逻辑漏洞:React组件每次渲染都会完整执行函数体,你在组件顶层声明的center_x、center_y变量每次都会被重新初始化,之前useEffect中赋值的内容会直接丢失。如果你的绘制逻辑在useEffect外部执行,根本无法拿到正确的计算值,严格模式下的重复渲染还会进一步放大时序问题,eslint的警告属于合理的错误提示,这种写法是典型的React反模式,不建议使用。
2. useState
useState仅适合存储需要触发组件重新渲染的状态。canvas绘制参数仅供给canvas绘图逻辑使用,不需要触发视图更新,每次调用setState都会带来不必要的组件重渲染开销,频繁更新参数的场景下会产生大量冗余渲染,性能损耗非常明显,完全不适合高频率绘图的需求。
3. useRef
useRef是最匹配当前场景的方案:
- 性能开销极低:修改
ref.current属性不会触发组件重渲染,性能和直接使用变量几乎一致,没有额外的渲染负担 - 跨渲染周期保留值:不会因为组件重渲染被重置,同时又支持随时修改更新,完全适配canvas尺寸变化后参数重新计算的需求
- 逻辑可维护性高:所有绘制相关的可变参数可以统一存在同一个ref对象中,方便管理也不会出现变量丢失的问题
参考实现代码
import { useRef, useEffect } from 'react'; function TestElement(props) { const canvas = useRef(null); // 统一存储所有canvas绘制参数 const canvasParams = useRef({ center_x: 0, center_y: 0, // 其他需要的参数都可以放在这里 }); // 绘制逻辑封装 const draw = () => { if (!canvas.current) return; const ctx = canvas.current.getContext('2d'); const { center_x, center_y } = canvasParams.current; // 这里写具体绘制逻辑 ctx.clearRect(0, 0, canvas.current.width, canvas.current.height); ctx.arc(center_x, center_y, 50, 0, 2 * Math.PI); ctx.fillStyle = '#2196F3'; ctx.fill(); }; useEffect(() => { if (!canvas.current) return; // 计算参数 const { width, height } = canvas.current; canvasParams.current.center_x = width / 2; canvasParams.current.center_y = height / 2; // 执行绘制 draw(); // 如果需要监听canvas尺寸自动更新参数,可以加ResizeObserver const observer = new ResizeObserver((entries) => { const { width, height } = entries[0].contentRect; canvas.current.width = width; canvas.current.height = height; canvasParams.current.center_x = width / 2; canvasParams.current.center_y = height / 2; draw(); }); observer.observe(canvas.current); return () => observer.disconnect(); }, []); return <canvas className='visualizer' ref={canvas} />; }
内容的提问来源于stack exchange,提问作者nreh
相关产品推荐
相关产品推荐

