You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件结合KonvaJS重渲染时内存占用升高的原因及解决方法

React+Konva组件重渲染内存升高的原因与解决方法

内存升高的核心原因

  • 每次device状态变化触发组件重渲染时,useEffect会执行gameStart,重复创建新的Konva Stage、Layer实例,但旧实例未被销毁:Konva的Stage会绑定到gamePanel DOM节点,旧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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 07:53:12