如何在Nuxt.js路由切换后重新初始化Matter.js实例?
解决Nuxt.js中Matter.js路由切换后失效的问题
你的问题出在两个核心点:一是DOMContentLoaded事件仅在页面首次加载时触发,路由切换返回时不会再次执行;二是未清理旧的Matter实例,导致新实例与残留资源冲突。按以下步骤修改即可修复:
1. 调整初始化逻辑,移除冗余监听
mounted钩子触发时组件DOM已渲染完成,无需等待DOMContentLoaded,直接调用初始化方法。同时将Matter核心实例(引擎、运行器、动画帧ID等)存入组件数据,方便后续销毁操作。
2. 添加销毁钩子,彻底清理旧实例
在组件销毁前,停止Matter运行器、销毁引擎、取消所有动画帧请求,同时重置元素样式,避免下次进入页面时残留旧状态。
修改后的完整组件代码
export default { data() { return { items: [ { className: "-i1" }, { className: "-i2" }, { className: "-i3" }, { className: "-i4" }, { className: "-i5" }, ], // 存储Matter实例,用于销毁阶段清理 matterInstance: { engine: null, runner: null, animationFrames: [], }, }; }, mounted() { // 组件挂载后直接初始化,无需等待DOMContentLoaded this.startFallbox(); }, beforeDestroy() { const { engine, runner, animationFrames } = this.matterInstance; // 停止Matter运行器 if (runner) Matter.Runner.stop(runner); // 销毁引擎,释放资源 if (engine) Matter.Engine.clear(engine); // 取消所有动画帧请求,避免内存泄漏 animationFrames.forEach(id => cancelAnimationFrame(id)); // 重置元素样式,恢复初始状态 this.items.forEach(i => { const elem = document.querySelector(`.${i.className}`); if (elem) { elem.style.opacity = '0'; elem.style.top = ''; elem.style.left = ''; elem.style.transform = ''; } }); }, methods: { startFallbox() { const Engine = Matter.Engine; const Render = Matter.Render; const Runner = Matter.Runner; const Bodies = Matter.Bodies; const Body = Matter.Body; const Composite = Matter.Composite; const MouseConstraint = Matter.MouseConstraint; const engine = Engine.create(); const world = engine.world; engine.gravity.y = 1; const fallbox = document.querySelector(".fallbox-scene"); const render = Render.create({ element: fallbox, engine, options: { width: fallbox.offsetWidth, height: fallbox.offsetHeight, }, }); const runner = Runner.create(); Runner.run(runner, engine); // 将核心实例存入data,供销毁时使用 this.matterInstance.engine = engine; this.matterInstance.runner = runner; this.matterInstance.animationFrames = []; const itemArray = this.items; itemArray.forEach((i) => { const get = document.querySelector(`.${i.className}`); if (!get) return; get.style.opacity = 1; const item = { w: get.clientWidth, h: get.clientHeight, body: Bodies.rectangle( Math.random() * window.innerWidth, Math.random() * -1000, get.clientWidth, get.clientHeight, { restitution: 0.5, angle: Math.random() * 360, } ), elem: get, render() { const { x, y } = this.body.position; this.elem.style.top = `${y - this.h / 2}px`; this.elem.style.left = `${x - this.w / 2}px`; this.elem.style.transform = `rotate(${this.body.angle}rad)`; }, }; Body.rotate(item.body, Math.random() * 360); Composite.add(world, [item.body]); // 存储动画帧ID,方便销毁时取消 const rerender = () => { item.render(); const id = requestAnimationFrame(rerender); this.matterInstance.animationFrames.push(id); }; rerender(); }); const ground = Bodies.rectangle( fallbox.offsetWidth / 2, fallbox.offsetHeight, 2000, 1, { isStatic: true } ); const left = Bodies.rectangle( 0, fallbox.offsetHeight / 2, 1, fallbox.offsetHeight, { isStatic: true } ); const right = Bodies.rectangle( fallbox.offsetWidth, fallbox.offsetHeight / 2, 1, fallbox.offsetHeight, { isStatic: true } ); Composite.add(world, [ground, left, right]); const mouseConstraint = MouseConstraint.create(engine, { element: fallbox, constraint: { stiffness: 0.2 }, }); mouseConstraint.mouse.element.removeEventListener( "mousewheel", mouseConstraint.mouse.mousewheel ); mouseConstraint.mouse.element.removeEventListener( "DOMMouseScroll", mouseConstraint.mouse.mousewheel ); Composite.add(world, mouseConstraint); Render.lookAt(render, { min: { x: 0, y: 0 }, max: { x: fallbox.offsetWidth, y: fallbox.offsetHeight }, }); }, }, };
关键修改说明
- 移除DOMContentLoaded监听:
mounted阶段组件DOM已就绪,直接初始化即可,避免路由切换后事件不触发的问题。 - 实例存储与清理:将Matter核心实例存入组件数据,在
beforeDestroy中彻底销毁,避免内存泄漏和实例冲突。 - 元素状态重置:销毁时恢复元素初始样式,确保下次进入页面时是全新状态。
- 选择器优化:将
getElementsByClassName替换为querySelector,简化逻辑并避免集合操作的潜在问题。
内容的提问来源于stack exchange,提问作者Philippe
相关产品推荐
相关产品推荐

