React组件结合KonvaJS重渲染时内存占用升高的原因及解决方法
React+Konva组件重渲染内存升高的原因与解决方法
内存升高的核心原因
- 每次
device状态变化触发组件重渲染时,useEffect会执行gameStart,重复创建新的Konva Stage、Layer实例,但旧实例未被销毁:Konva的Stage会绑定到gamePanelDOM节点,旧Stage的DOM元素、对象引用依然留在内存中,浏览器无法自动回收。 - 图片加载的
onload回调及内部创建的Konva元素,在组件重渲染时未被清理,这些引用会阻止垃圾回收机制回收相关内存。 - 未处理Konva实例的生命周期,比如Stage、Layer没有调用
destroy()方法释放资源,导致内存持续堆积。
解决步骤
1. 用useRef保存Konva实例引用,方便清理
通过useRef存储Stage、Layer和图片实例,这样在组件重渲染或卸载时能获取到旧实例并销毁。
2. 在useEffect的清理函数中销毁旧实例
每次useEffect执行前(或组件卸载时),先销毁之前创建的Konva实例,释放DOM和内存资源。
3. 优化图片加载回调,避免内存泄漏
在组件卸载或重渲染时,取消图片的onload监听,切断不必要的引用。
修改后的代码示例:
import React, { useState, useEffect, useSelector, useRef } from "react"; import { Stage as StageKonva } from "konva/lib/Stage"; import { Line as LineKonva } from "konva/lib/shapes/Line"; import { Animation as AnimationKonva } from "konva/lib/Animation"; import { Layer as LayerKonva } from "konva/lib/Layer"; import { Image as ImageKonva } from "konva/lib/shapes/Image"; export const Abc = (data)=>{ const { device } = useSelector((state) => ({ device: state.device })); // 用useRef存储Konva实例 const stageRef = useRef(null); const layerRef = useRef(null); const imageObjRef = useRef(null); useEffect(() => { // 先清理旧实例 if (stageRef.current) { stageRef.current.destroy(); stageRef.current = null; } if (layerRef.current) { layerRef.current.destroy(); layerRef.current = null; } if (imageObjRef.current) { // 取消图片的onload监听 imageObjRef.current.onload = null; imageObjRef.current = null; } gameStart(); // 组件卸载时的清理逻辑 return () => { if (stageRef.current) { stageRef.current.destroy(); } if (layerRef.current) { layerRef.current.destroy(); } if (imageObjRef.current) { imageObjRef.current.onload = null; } }; },[device, data.image]) async function gameStart (){ stageRef.current = new StageKonva({ container: "gamePanel", width: 300, height: 300}); layerRef.current = new LayerKonva(); stageRef.current.add(layerRef.current); imageObjRef.current = new Image(); imageObjRef.current.onload = function () { let square = new ImageKonva({ x: 0, y: 0, height: 40, width: 20, image: imageObjRef.current }); // ------------------------------ // ---------Some code here------- // ------------------------------ layerRef.current.add(square); layerRef.current.draw(); } imageObjRef.current.src = data.image; } return(<div id="gamePanel"></div>); }
额外优化建议
- 如果
device变化不需要完全重建Stage,可以考虑只更新Stage的配置或元素,而非销毁重建,进一步减少内存开销。 - 避免在
gameStart中重复创建不必要的Konva元素,比如将静态元素提前创建并复用。
内容的提问来源于stack exchange,提问作者Pradeep G
相关产品推荐
相关产品推荐

