为何我的Phaser/React应用中会生成多个Canvas?
React + Phaser3 重复Canvas与useEffect多次执行问题修复
问题根源
- React 18开发环境下的严格模式会强制
useEffect执行两次,用于检测未清理的副作用,你的代码没有销毁游戏实例的逻辑,导致两次创建生成两个Canvas。 - 用
useState存储游戏状态会触发组件重渲染,且依赖数组包含game,导致setGame(true)后再次触发useEffect,逻辑冗余。 - 组件未渲染Phaser挂载的父容器
div#survival-game,Phaser默认会将Canvas添加到body,增加重复风险。
修复方案
- 用
useRef存储游戏实例:ref不会触发组件重渲染,能持久保存游戏实例,避免不必要的重渲染和状态循环。 - 添加清理函数:在组件卸载或effect重新触发时销毁Phaser游戏,释放资源并清除旧Canvas。
- 移除不必要的依赖:将
useEffect的依赖数组设为空,确保只执行一次;或者用useRef的标志位判断是否已初始化。 - 渲染Phaser挂载的父容器:在App组件中返回带有
id="survival-game"的div,让Phaser将Canvas挂载到指定位置。
修正后的代码
import "./App.css"; import Phaser from "phaser"; import PhaserMatterCollisionPlugin from "phaser-matter-collision-plugin"; import { useEffect, useRef } from "react"; function App() { // 用ref存储游戏实例,避免触发重渲染 const gameRef = useRef(null); useEffect(() => { // 已初始化则跳过 if (gameRef.current) return; const phaserGame = new Phaser.Game({ width: 512, height: 412, backgroundColor: "#333333", type: Phaser.AUTO, parent: "survival-game", scene: [], scale: { zoom: 2, }, physics: { default: "matter", matter: { debug: true, gravity: { y: 0 }, }, }, plugins: { scene: [ { plugin: PhaserMatterCollisionPlugin, key: "matterCollision", mapping: "matterCollision", }, ], }, }); gameRef.current = phaserGame; // 清理函数:组件卸载时销毁游戏 return () => { if (gameRef.current) { gameRef.current.destroy(true); gameRef.current = null; } }; }, []); // 空依赖数组,确保只执行一次 // 必须返回挂载Phaser的父容器 return <div id="survival-game"></div>; } export default App;
额外说明
- React18开发环境下严格模式的两次执行是正常现象,添加清理函数后,第一次创建的游戏会被销毁,最终只会保留一个实例。生产环境下严格模式自动关闭,不会出现这种情况。
- 用
gameRef.current判断是否已初始化,比state更高效,不会触发组件重渲染。 - 调用
game.destroy(true)会彻底清除游戏的DOM元素和资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Claire Cashmore
相关产品推荐
相关产品推荐

