如何在React项目中使用react-script-tag与react-router-dom嵌入p5js草图
问题原因
两个核心问题导致你现在的异常:
- 路由跳转不加载:React Router是客户端无刷新路由,
react-script-tag的加载逻辑没有适配React组件的挂载生命周期,只有整页刷新时浏览器才会主动执行脚本资源,客户端切路由时脚本不会自动触发执行。 - 跳转后草图残留:p5.js默认全局模式下会把生成的canvas直接追加到页面
<body>末尾,不属于你写的.p5js容器的子元素,路由切换时你的页面组件被卸载,但全局挂载的canvas和p5的帧循环、事件监听都不会被自动清理,所以会一直留在页面上。
解决方案
不要用react-script-tag引外部脚本,用p5官方的实例模式配合React生命周期处理加载和卸载,这是React项目集成p5最稳定的方式,步骤如下:
- 安装p5依赖
执行命令安装p5 npm包,比外链脚本更适配单页应用的构建流程:
npm install p5
- 修改p5草图为实例模式
把你原来的sketch2.js从全局写法改成实例模式,导出草图函数,避免污染全局作用域,同时方便后续控制挂载位置和销毁。
原全局写法参考:
// 原全局模式写法 function setup() { createCanvas(400, 400); } function draw() { background(220); // 你的绘制逻辑 }
修改后的实例模式:
// 修改后的sketch2.js export default function sketch(p) { p.setup = function () { // 后续会指定canvas挂载到对应容器,不会直接插在body末尾 p.createCanvas(800, 600); } p.draw = function () { p.background(220); // 原有绘制逻辑保留,所有p5内置方法前面加p.前缀即可 } }
- 重写p5js页面组件
用useRef保存p5实例和容器引用,useEffect处理组件挂载时的初始化、卸载时的清理逻辑,注意React组件名需要大写开头:
import React, { useEffect, useRef } from 'react'; import p5 from 'p5'; // 引入修改后的实例模式草图,注意路径匹配你本地的文件位置 import sketch from '../sketch2.js'; const P5jsPage = () => { const containerRef = useRef(null); const p5InsRef = useRef(null); useEffect(() => { // 组件挂载(即路由跳转到/p5js)时,初始化p5实例,挂载到指定容器 p5InsRef.current = new p5(sketch, containerRef.current); // 组件卸载(即路由切走)时,销毁p5实例,移除canvas、帧循环和事件监听 return () => { p5InsRef.current.remove(); } }, []); return <div className="p5js" ref={containerRef}></div> } export default P5jsPage;
- 修正App.js里的路由引用
因为组件名改成了大写开头的P5jsPage,对应调整导入和路由配置:
import { BrowserRouter, Routes, Route } from "react-router-dom"; import "./App.css"; import Navbar from "./components/Navbar"; import Home from "./pages/Home"; import SQL from "./pages/SQL"; import SuperCollider from "./pages/SuperCollider"; // 调整导入名 import P5jsPage from "./pages/p5js"; function App() { return ( <> <BrowserRouter> <Navbar /> <Routes> <Route path="/" element={<Home />} /> <Route path="/sql" element={<SQL />} /> <Route path="/sc" element={<SuperCollider />} /> {/* 调整路由element */} <Route path="/p5js" element={<P5jsPage />} /> </Routes> </BrowserRouter> </> ); }; export default App;
改完后效果:
- 路由跳转到
/p5js时组件自动挂载,p5草图会立即初始化渲染,不需要手动刷新页面 - 跳转到其他路由时,组件卸载逻辑会自动销毁p5实例、清除canvas,不会有残留
- 不会污染全局作用域,也不会因为p5的帧循环持续运行导致页面内存泄漏
如果你坚持要保留原有的外链脚本+全局模式写法,需要自己在useEffect里手动创建script标签插入DOM,卸载时手动删除script、查找删除body下p5生成的canvas、清除window上挂载的p5全局方法和requestAnimationFrame循环,这种方式容易遗漏清理逻辑造成内存泄漏,不推荐使用。
内容的提问来源于stack exchange,提问作者spal
相关产品推荐
相关产品推荐

