本文 AI 產出,尚未審核

Three.js 實戰專案:3D 互動網站 — 部署成完整作品


簡介

在完成了 3D 場景的開發、互動邏輯與 UI 設計之後,把作品部署上線 才是真正讓使用者體驗的關鍵一步。
現代前端開發已不僅是把 index.htmlmain.js 直接放到伺服器,而是需要考慮 模組打包、資源最佳化、CDN 加速、安全性與持續部署 等完整流程。

本篇文章將以 Three.js 為核心,從 開發環境建置靜態資源管理部署平台選擇、到 常見陷阱與最佳實踐,一步步說明如何把一個 3D 互動網站變成可供大眾直接訪問的完整作品,讓初學者也能快速上手、進階開發者能夠在實務上得到參考。


核心概念

1. 使用模組打包工具 (Vite / Webpack)

在瀏覽器原生支援 ES6 模組前,前端專案多半依賴打包工具將多個檔案合併、壓縮、轉譯。即使現在大多瀏覽器已支援模組,打包仍能提供以下好處

  • Tree‑shaking:只保留實際使用到的 Three.js 子模組,減少程式碼體積。
  • 資源載入:自動把模型、貼圖、HDR 環境圖等檔案作為模組匯入,產生雜湊檔名以利快取。
  • 開發伺服器:熱更新 (HMR) 讓你修改程式碼即時在瀏覽器中看到變化。

Vite 範例 (vite.config.js)

import { defineConfig } from 'vite';
import { resolve } from 'path';

// https://vitejs.dev/config/
export default defineConfig({
  // 設定別名,方便引用 assets
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
      '@assets': resolve(__dirname, 'src/assets')
    }
  },
  // 讓 three.js 的 ES 模組正確被編譯
  optimizeDeps: {
    include: ['three']
  },
  build: {
    // 產出目錄
    outDir: 'dist',
    // 壓縮程式碼
    minify: 'esbuild',
    // 產生雜湊檔名,利於瀏覽器快取
    assetsDir: 'assets',
    rollupOptions: {
      output: {
        // 讓模型、貼圖等檔案放在 assets 子目錄
        assetFileNames: 'assets/[name].[hash][extname]'
      }
    }
  }
});

重點optimizeDeps.include 告訴 Vite 先預先編譯 Three.js,避免在開發階段出現 Cannot find module 'three' 的錯誤。


2. 動態載入大型模型與貼圖

3D 作品的模型檔案往往數 MB 甚至十幾 MB,若一次性全部載入會造成 首屏渲染卡頓。解法是使用 import() 或 Three.js 的 GLTFLoader 搭配 懶加載

動態載入範例

// main.js
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

// 建立基本場景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 1000);
camera.position.set(0, 1.6, 3);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

// 當使用者點擊「載入模型」按鈕時才真正下載
document.getElementById('loadBtn').addEventListener('click', async () => {
  // 使用動態匯入,讓 webpack/Vite 能自動分割程式碼
  const { GLTFLoader } = await import('three/examples/jsm/loaders/GLTFLoader');
  const loader = new GLTFLoader();

  // 讀取 glb 檔案,檔案會自動走 CDN / 靜態伺服器
  loader.load('/assets/models/room.glb', (gltf) => {
    scene.add(gltf.scene);
    console.log('模型載入完成');
  });
});

技巧await import(...) 只在支援 ES2020 的瀏覽器上有效,若要兼容舊版瀏覽器,可在打包時加入 polyfill。


3. 使用 CDN 加速靜態資源

把模型、HDR 環境貼圖、字體等檔案放在 CDN(例如 Cloudflare、Netlify Large Media)能大幅降低伺服器負載,並利用地理分布的 Edge 節點提升下載速度。

CDN 設定範例(Netlify netlify.toml

[build]
  publish = "dist"
  command = "npm run build"

[[headers]]
  # 為所有資產加上長期快取
  for = "/assets/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[[redirects]]
  # 把所有 404 請求導向 index.html,支援 SPA
  from = "/*"
  to = "/index.html"
  status = 200

重點:將 Cache‑Control 設為 immutable 可讓瀏覽器在資源未變更時直接使用快取,減少重複下載。


4. 部署平台比較

平台 免費額度 靜態資源上傳 CI/CD 整合 特色
Netlify 每月 100 GB 帶寬、300 build minutes 支援自動壓縮、Cache‑Control 直接連接 GitHub/GitLab 一鍵部署、表單、伺服器端函式
Vercel 每月 100 GB 帶寬、100 GB‑hour Edge Functions、Smart CDN Git 整合、預覽環境 適合 Next.js、Serverless
GitHub Pages 無流量上限(受限於 GitHub) 只能部署靜態檔案 手動或 Action 自動化 簡單、適合個人作品集
Firebase Hosting 每月 10 GB 下載、10 GB 儲存 HTTPS、Cache‑Control 自訂 CI/CD via GitHub Action 支援多站點、即時預覽

建議:若專案需要 Serverless API(例如保存使用者設定),可以選擇 Netlify Functions 或 Vercel Edge Functions;若僅為純靜態 3D 網站,GitHub Pages 已足夠。


5. 加入 Service Worker 變成 PWA

將 3D 網站包裝成 Progressive Web App,使用者即使離線也能瀏覽已快取的模型。下面示範最簡單的 Service Worker 設定。

sw.js

const CACHE_NAME = 'threejs-pwa-v1';
const ASSETS_TO_CACHE = [
  '/',
  '/index.html',
  '/main.js',
  '/assets/models/room.glb',
  '/assets/textures/hdri.hdr'
];

// 安裝階段:快取核心資源
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS_TO_CACHE))
  );
});

// 取得資源時:先從快取取,沒有再向網路請求
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) => {
      return cached || fetch(event.request).then((response) => {
        // 動態快取新資源(可自行限制)
        const clone = response.clone();
        caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
        return response;
      });
    })
  );
});

註冊 Service Worker(main.js

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(() => console.log('PWA Service Worker 已註冊'))
    .catch((err) => console.error('註冊失敗', err));
}

注意sw.js 必須放在網站根目錄,否則快取範圍會受到限制。


常見陷阱與最佳實踐

陷阱 說明 解決方案
模型過大 未壓縮的 GLTF/OBJ 可能 > 10 MB,下載時間長。 使用 DracoMeshopt 壓縮,並在 GLTFLoader 加上 DRACOLoader
貼圖未設定 CORS 直接載入跨域貼圖會拋出 Cross‑origin 錯誤。 在 CDN 或伺服器設定 Access‑Control‑Allow‑Origin: *,或在 TextureLoader 加入 loader.setCrossOrigin('anonymous')
開發環境與生產環境路徑不一致 import.meta.env.BASE_URL 未正確設定導致資源 404。 在 Vite/webpack 中使用 process.env.PUBLIC_URLimport.meta.env.BASE_URL,並在 netlify.tomlvercel.json 中設定 rewrites
瀏覽器記憶體泄漏 持續創建大量 Mesh、未釋放 dispose() 在切換場景或移除物件時呼叫 geometry.dispose()material.dispose()texture.dispose()
HTTPS 與混合內容 Three.js 需要載入 HDR、GLB 等二進位檔案,若網站是 HTTPS 而資源是 HTTP,會被阻擋。 確保所有資源均使用 HTTPS,或在 Netlify/Vercel 設定自動轉向。

最佳實踐

  1. 使用 npm run build 產出最小化檔案,並檢查 bundle size(建議 < 500 KB gzipped)。
  2. 啟用 HTTP/2 或 HTTP/3,減少多檔案請求的延遲。
  3. 設定合理的 Cache‑Control:HTML 1 h、JS/CSS 1 day、模型/貼圖 1 year + immutable
  4. 結合 CI/CD:每次 push 後自動執行測試、建置、部署,保持網站始終可用。
  5. 監控效能:使用 Lighthouse、Web Vitals,特別留意 First Contentful Paint (FCP)Time to Interactive (TTI)

實際應用場景

場景 需求 部署建議
產品展示 (e‑commerce) 高畫質模型、快速切換顏色 使用 CDN + lazy‑load,模型分割成部件,利用 GLTFLoader 動態載入。
線上展覽/虛擬藝廊 大量 HDR 環境、音效、互動導覽 搭配 Service Worker 做離線快取,使用 Netlify Functions 提供導覽路徑 API。
教育訓練平台 多個教學案例、即時更新 透過 GitHub Actions 自動部署,利用 Vercel Preview Deployments 讓教師先行測試。
行銷活動 (微網站) 短期活動、需要快速上線 使用 Firebase Hostingfirebase deploy,結合 Google Analytics 追蹤互動。
企業內部儀表板 安全性、存取控制 部署在 Vercel 並設定 Password Protection,或使用 Netlify Identity 做身份驗證。

總結

  • 打包工具(Vite、Webpack)是現代 Three.js 專案不可或缺的基礎,能有效減少程式碼體積、管理資源、提供熱更新。
  • 懶加載與壓縮(Draco、Meshopt)是解決大型模型下載瓶頸的關鍵技巧。
  • CDN 與快取策略(Cache‑Control、immutable)讓使用者在全球任意地點都能快速取得模型與貼圖。
  • 部署平台(Netlify、Vercel、GitHub Pages、Firebase)各有特色,依需求選擇最適合的方案。
  • PWA 與 Service Worker 為 3D 網站提供離線體驗,提升使用者黏著度。
  • 避免常見陷阱(跨域、記憶體泄漏、路徑錯誤)並遵循最佳實踐,可讓作品在正式環境中保持穩定與高效。

透過本文的步驟與範例,您已掌握從 本機開發 → 打包 → 上傳 CDN → 部署至雲端 的完整流程,現在只要把自己的 3D 互動網站推上去,讓全世界的使用者盡情探索吧! 🎉