Three.js 資源追蹤器模式
建立於 2025年9月20日 更新於 2025年12月1日
three.js webgl memory-management
問題起源
在開發 Astro Island 架構 搭配 Three.js 場景時,我遇到一個棘手的問題:頁面切換後,GPU 記憶體不斷攀升。Chrome DevTools 的 Performance Monitor 顯示 JS heap 穩定成長,但 Three.js 本身不會自動釋放已建立的 GPU 資源。
我一開始嘗試在 dispose() 裡手動逐一清理每個 geometry、material、texture。但隨著場景複雜度提升,漏掉某個資源是遲早的事。於是我改用集中式追蹤的方式。
ResourceTracker 模式
核心想法很直觀:建立一個 tracker 物件,所有透過 Three.js 建立的 GPU 資源都先經過它註冊。銷毀時只要呼叫一次 tracker.dispose(),就能確保全部釋放。
class ResourceTracker {
private resources = new Set<{ dispose: () => void }>();
track<T extends { dispose: () => void }>(resource: T): T {
this.resources.add(resource);
return resource;
}
dispose(): void {
for (const resource of this.resources) {
resource.dispose();
}
this.resources.clear();
}
}
使用方式:
const tracker = new ResourceTracker();
const geometry = tracker.track(new THREE.BoxGeometry(1, 1, 1));
const texture = tracker.track(new THREE.TextureLoader().load('/textures/diffuse.webp'));
const material = tracker.track(new THREE.MeshStandardMaterial({ map: texture }));
const mesh = new THREE.Mesh(geometry, material);
// 頁面離開時
tracker.dispose();
與 Astro 生命週期整合
在 Astro 的 View Transitions 架構下,頁面切換觸發 astro:before-swap 事件。我在這個時機點執行全部清理:
document.addEventListener('astro:before-swap', () => {
tracker.dispose();
renderer.dispose();
renderer.forceContextLoss();
});
同時也要處理 webglcontextlost 事件。WebGL context 可能因為瀏覽器資源壓力被回收,這不是錯誤,而是需要優雅處理的正常情境。
實際觀察
導入 ResourceTracker 後,在頁面間來回切換 50 次,GPU 記憶體維持在穩定範圍內,不再有累積性成長。這個模式適用於任何需要動態建立/銷毀 Three.js 場景的 SPA 或 MPA 架構。
待深入
- 對
InstancedMesh的 buffer 追蹤是否需要特殊處理 - render target 的清理時機
- 在 Blender 到 Web 管線 中,透過
GLTFLoader載入的模型如何自動追蹤其內部所有資源