You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React Native iOS端WebView加载Three.js模型内存未释放求助

解决React Native WebView加载Three.js模型的内存泄漏问题

嘿,这个问题我之前帮好几个开发者处理过!React Native的WebView切换页面后内存没释放,核心原因是Three.js的WebGL资源(几何体、材质、纹理这些)没有被正确销毁,而且WebView的内核还在持有这些资源引用。下面给你一套完整的解决方案:

第一步:在Three.js代码中实现彻底的资源清理函数

你只把scene = null是远远不够的,Three.js的很多对象需要手动调用dispose()才能释放WebGL内存。写一个专门的清理函数:

// 假设你的全局变量是scene, camera, renderer, controls(如果用了轨道控制器)
function cleanupThreeJS() {
  // 1. 清理场景中的所有子对象
  while (scene.children.length > 0) {
    const child = scene.children[0];
    scene.remove(child);

    // 销毁几何体(如果有)
    if (child.geometry) {
      child.geometry.dispose();
    }

    // 销毁材质(注意材质可能是数组,比如MeshFaceMaterial)
    if (child.material) {
      if (Array.isArray(child.material)) {
        child.material.forEach(material => material.dispose());
      } else {
        child.material.dispose();
      }
    }

    // 如果是其他类型对象(比如灯光、雾),直接移除即可,无需额外销毁
  }

  // 2. 销毁渲染器并移除DOM元素
  if (renderer) {
    renderer.dispose(); // 释放WebGL上下文资源
    const canvas = renderer.domElement;
    if (canvas.parentNode) {
      canvas.parentNode.removeChild(canvas); // 从DOM中移除canvas
    }
    renderer = null;
  }

  // 3. 清理相机和控制器
  if (camera) {
    camera = null;
  }
  if (controls) { // 比如OrbitControls
    controls.dispose();
    controls = null;
  }

  // 4. 移除全局事件监听(比如窗口 resize 监听)
  window.removeEventListener('resize', onWindowResize);

  // 5. 最后清空场景引用
  scene = null;
}

第二步:在WebView的HTML中绑定页面卸载事件

在加载Three.js的HTML文件里,添加页面卸载时的触发逻辑,确保WebView关闭时自动执行清理:

<script>
// ...你的Three.js初始化代码

// 页面即将卸载时执行清理
window.onbeforeunload = () => {
  cleanupThreeJS();
};

// 也可以监听React Native发来的消息触发清理
window.addEventListener('message', (event) => {
  if (event.data === 'cleanupThreeJS') {
    cleanupThreeJS();
  }
});
</script>

第三步:在React Native组件中触发清理

当你切换页面(组件卸载)时,主动通知WebView执行清理函数,确保WebView的内核及时释放资源:

函数组件示例:

import React, { useEffect, useRef } from 'react';
import { WebView } from 'react-native-webview';

const ThreeJSViewer = () => {
  const webViewRef = useRef(null);

  // 组件卸载时触发Three.js清理
  useEffect(() => {
    return () => {
      if (webViewRef.current) {
        // 方式1:直接注入JS执行清理
        webViewRef.current.injectJavaScript(`cleanupThreeJS(); true;`);
        // 方式2:通过postMessage发送消息(对应HTML中的message监听)
        // webViewRef.current.postMessage('cleanupThreeJS');
      }
    };
  }, []);

  return (
    <WebView
      ref={webViewRef}
      source={{ uri: 'file:///android_asset/threejs-scene.html' }} // 本地HTML路径,iOS对应路径调整
      javaScriptEnabled={true}
      domStorageEnabled={true}
    />
  );
};

export default ThreeJSViewer;

关键注意点:

  • 不要让WebView组件被隐藏(比如用display: none),一定要让它在页面切换时完全卸载,否则内核会一直持有资源。
  • 如果你的WebView是用路由管理的(比如React Navigation),确保路由切换时组件被卸载(而非缓存),可以在路由配置中设置unmountOnBlur: true

为什么这能解决问题?

Three.js的WebGL资源(几何体、材质等)是存储在浏览器的WebGL上下文中的,普通的JS垃圾回收不会处理这些资源,必须手动调用dispose()才能释放。同时,React Native的WebView如果不主动触发清理,即使组件看起来被隐藏,内核进程依然会持有这些资源,导致内存泄漏。

内容的提问来源于stack exchange,提问作者Ashok R

火山引擎 最新活动