本文 AI 產出,尚未審核

Three.js 部署與相容性:使用 CDN 或雲端部署

簡介

在完成 Three.js 應用的開發後,部署 成為讓使用者真正看到 3D 效果的最後一步。
傳統上,我們會將整個專案打包成靜態檔案,放在自己的伺服器或是本機環境測試。但隨著前端資源日益龐大、瀏覽器相容性需求提升,使用 CDN(Content Delivery Network)或雲端平台 成為更常見且高效的部署方式。

本文將帶你了解 為什麼如何 透過 CDN 或雲端服務部署 Three.js,並說明在相容性、效能與維護上需要注意的要點。適合剛學完 Three.js 基礎的初學者,也適用於想要優化部署流程的中階開發者。


核心概念

1. CDN 與雲端部署的差異

項目 CDN 雲端平台(如 Vercel、Netlify、Firebase Hosting)
目的 快速分發靜態資源(JS、CSS、模型檔) 完整的前端部署環境,支援 CI/CD、伺服器端渲染
優點 全球節點、低延遲、免自行維護伺服器 自動化部署、簡易設定環境變數、支援動態 API
缺點 只適合靜態檔案,無法直接處理後端邏輯 部署成本(免費方案有流量限制)
典型使用情境 引入 Three.js 核心庫、載入常用模型或貼圖 部署完整的 Three.js 網站、結合後端服務

結論:在大多數案例中,把 Three.js 程式庫放在 CDN網站本身則部署於雲端平台,可同時取得兩者的優勢。

2. 為什麼要使用 CDN 引入 Three.js

  1. 減少首次載入時間:CDN 會自動根據使用者所在地選擇最近的節點,降低 RTT(Round‑Trip Time)。
  2. 瀏覽器快取共享:若使用者之前已訪問過其他使用同一 CDN 的網站,瀏覽器會直接使用快取,避免重複下載。
  3. 自動版本管理:許多 CDN(如 unpkgjsDelivr)支援語義化版本號,讓你輕鬆升級或回退。

3. 常見 CDN 來源

CDN URL 範例 說明
jsDelivr https://cdn.jsdelivr.net/npm/three@0.166.0/build/three.min.js 支援 npm 套件直接引用,支援自動選擇最適合的壓縮檔
unpkg https://unpkg.com/three@0.166.0/build/three.min.js 直接從 npm 下載,支援 ?module 取得 ES Module
cdnjs https://cdnjs.cloudflare.com/ajax/libs/three.js/r166/three.min.js 老牌 CDN,提供多個發佈版(rXXX)

小技巧:在開發階段使用 ?module(如 https://unpkg.com/three@0.166.0/build/three.module.js?module)可直接以 ES Module 方式匯入,配合 <script type="module"> 使用。

4. 雲端平台部署流程概覽

以下以 Vercel 為例,說明從 GitHub 到線上網站的完整流程:

  1. 建立 Git Repository:把 Three.js 專案(HTML、JS、模型檔)推上 GitHub。
  2. 連結 Vercel 帳號:在 Vercel 網站點選「New Project」→ 選擇剛才的 repo。
  3. 設定 Build 設定:對於純靜態網站,Vercel 會自動偵測 public/ 或根目錄的 index.html,不需要額外建置指令。
  4. 部署:點擊「Deploy」後,Vercel 會自動建立 CDN 網路,並提供唯一的 domain(如 your-project.vercel.app)。
  5. 自訂域名(可選):在 Vercel 控制台設定自己的域名,並透過 DNS 解析指向 Vercel。

其他平台(Netlify、Firebase Hosting)操作類似,只是 UI 與設定檔稍有差異。

5. 程式碼範例

以下示範 三種不同的部署方式,從最簡單的 CDN 引入到完整的雲端部署流程。每段程式碼均附上說明註解,方便直接複製使用。

範例 1:使用 CDN 引入 Three.js(最小化 HTML)

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">
  <title>Three.js CDN 範例</title>
  <style>
    body { margin:0; overflow:hidden; }
    canvas { display:block; }
  </style>
</head>
<body>
  <!-- 直接從 jsDelivr 取得壓縮版 -->
  <script src="https://cdn.jsdelivr.net/npm/three@0.166.0/build/three.min.js"></script>
  <script>
    // 基本場景、相機、渲染器
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(innerWidth, innerHeight);
    document.body.appendChild(renderer.domElement);

    // 簡單立方體
    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshBasicMaterial({ color: 0x0077ff });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
    camera.position.z = 3;

    function animate() {
      requestAnimationFrame(animate);
      cube.rotation.x += 0.01;
      cube.rotation.y += 0.01;
      renderer.render(scene, camera);
    }
    animate();
  </script>
</body>
</html>

重點:只要把 script src 換成其他 CDN(如 unpkg),即可測試不同來源的效能差異。

範例 2:使用 ES Module(type="module")從 CDN 載入 Three.js

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">
  <title>Three.js ES Module CDN 範例</title>
  <style>body{margin:0;overflow:hidden}</style>
</head>
<body>
  <script type="module">
    // 從 unpkg 直接取得 ES Module 版本
    import * as THREE from 'https://unpkg.com/three@0.166.0/build/three.module.js?module';

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(innerWidth, innerHeight);
    document.body.appendChild(renderer.domElement);

    const geometry = new THREE.SphereGeometry(1, 32, 32);
    const material = new THREE.MeshStandardMaterial({ color: '#ff6600', metalness: 0.5, roughness: 0.2 });
    const sphere = new THREE.Mesh(geometry, material);
    scene.add(sphere);

    // 加入簡易光源
    const light = new THREE.DirectionalLight(0xffffff, 1);
    light.position.set(5, 5, 5);
    scene.add(light);

    camera.position.set(0, 0, 3);

    function render() {
      requestAnimationFrame(render);
      sphere.rotation.y += 0.005;
      renderer.render(scene, camera);
    }
    render();
  </script>
</body>
</html>

說明:使用 ES Module 可以直接在瀏覽器端使用 import,讓程式碼結構更清晰,且支援 tree‑shaking(如果未使用的功能會被瀏覽器自動忽略)。

範例 3:在 Vercel 部署的完整 Three.js 靜態網站

下面的檔案結構示意(檔案放在專案根目錄):

my-threejs-project/
│─ public/
│   │─ index.html
│   │─ assets/
│   │   └─ model.glb
│   └─ styles.css
│─ package.json   (可不需要,但放在此方便未來加入 build 工具)
└─ .vercelignore  (可選,排除不必要檔案)

public/index.html(使用 CDN + GLTFLoader)

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">
  <title>Three.js + Vercel 部署範例</title>
  <link rel="stylesheet" href="/styles.css">
</head>
<body>
  <script src="https://cdn.jsdelivr.net/npm/three@0.166.0/build/three.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/three@0.166.0/examples/js/loaders/GLTFLoader.js"></script>
  <script>
    // 基本設定
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(70, innerWidth / innerHeight, 0.1, 100);
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(innerWidth, innerHeight);
    document.body.appendChild(renderer.domElement);

    // 環境光 + 方向光
    scene.add(new THREE.AmbientLight(0xffffff, 0.6));
    const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
    dirLight.position.set(5, 10, 7);
    scene.add(dirLight);

    // 載入 GLTF 模型
    const loader = new THREE.GLTFLoader();
    loader.load('/assets/model.glb',
      gltf => {
        const model = gltf.scene;
        model.scale.set(0.5, 0.5, 0.5);
        scene.add(model);
      },
      xhr => console.log(`模型載入進度: ${(xhr.loaded / xhr.total * 100).toFixed(1)}%`),
      err => console.error('模型載入失敗', err)
    );

    camera.position.set(0, 1, 3);
    const controls = new THREE.OrbitControls(camera, renderer.domElement); // 若使用 OrbitControls,請自行加入 CDN

    function animate() {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
    }
    animate();
  </script>
</body>
</html>

public/styles.css(簡單全螢幕樣式)

html, body, #app {
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
  background: #111;
}

部署步驟(簡化版):

# 1. 初始化 Git
git init
git add .
git commit -m "Initial commit - Three.js with CDN"

# 2. 推送至 GitHub
git remote add origin https://github.com/yourname/my-threejs-project.git
git push -u origin master

# 3. 前往 Vercel
# - 登入後點選 "New Project"
# - 選擇剛剛的 repo
# - Vercel 會自動偵測為靜態網站,直接 Deploy

部署完成後,你會得到 https://my-threejs-project.vercel.app,全球 CDN 會自動快取 three.min.js、模型檔與 CSS,使用者即時取得最佳效能。

範例 4:使用 Netlify 搭配自動化 CI/CD

# netlify.toml
[build]
  publish = "public"   # 指定要發佈的資料夾
  command = "echo 'No build step needed'"  # 靜態網站不需要編譯

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

netlify.toml 放在專案根目錄,接著在 Netlify 網站「New site from Git」選擇 repo,完成後 Netlify 會自動部署,且每次 push 都會觸發重新部署。


常見陷阱與最佳實踐

陷阱 可能的症狀 解決方案 / 最佳實踐
CDN 版本不一致 產生 THREE.Object3D 無法正確繼承或 GLTFLoader 报错 固定版本號(如 @0.166.0),不要直接使用 latest;在 package.json 中鎖定相同版本,確保本地與線上環境一致。
跨域(CORS)問題 無法載入模型、貼圖或 HDR 環境圖 確保模型檔案放在同一域名或使用支援 CORS 的 CDN;若自行上傳到 Cloud Storage,必須在設定中開啟 Access-Control-Allow-Origin: *
Cache 失效 更新模型後使用者仍看到舊檔 為資源加上 版本化 query string(例如 model.glb?v=20251101),或在雲端平台設定 Cache‑Controlno-cache(開發階段)。
載入順序錯誤 GLTFLoader 找不到 THREE 物件 先載入 three.min.js,再載入 GLTFLoader.js;或改用 ES Module 方式一次匯入(import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js')。
過大的模型檔 首次載入時間超過 5 秒,使用者離開 使用 glTF Draco 壓縮、GLB 二進位格式,或在 CDN 上啟用 gzip / brotli 壓縮。

最佳實踐清單

  1. 固定 CDN 版本:在 script src 中寫完整的語義化版本(例:@0.166.0),避免因新版破壞相容性。
  2. 使用 async / defer:若使用多個外部腳本,加入 defer 讓瀏覽器在解析完 HTML 後才執行,提升首次渲染速度。
    <script src="https://cdn.jsdelivr.net/npm/three@0.166.0/build/three.min.js" defer></script>
    
  3. 啟用 HTTP/2:大多數雲端平台自動支援 HTTP/2,能同時傳送多個小檔案(如貼圖)而不產生阻塞。
  4. 使用 preconnectdns-prefetch:提前與 CDN 建立連線,降低延遲。
    <link rel="preconnect" href="https://cdn.jsdelivr.net">
    <link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
    
  5. 監控效能:在 Chrome DevTools 的「Network」面板觀察資源大小、快取命中率;使用 Lighthouse 量化「First Contentful Paint」與「Time to Interactive」。

實際應用場景

場景 為何選擇 CDN + 雲端部署 範例作品
產品展示網站 大量模型與高解析度貼圖需要快速下載,且全球客戶可能同時訪問 3D 商品展覽、家具 AR 預覽
教育與互動教學平台 常常需要即時更新教材(模型、影片),使用 CDN 可免除每次重新部署的等待 WebGL 互動課程、科學模擬
藝術與視覺化專案 藝術家希望作品能在任何裝置上流暢播放,使用雲端平台的自動縮放與 CDN 能確保一致體驗 交互式裝置藝術、資料視覺化儀表板
行銷活動與微網站 活動期間流量激增,CDN 能自動擴展,雲端平台提供即時部署與回滾功能 節日優惠 3D 微站、品牌互動體驗

案例分享:某家具品牌使用 Vercel + jsDelivr,把所有 GLB 模型放在 Cloudflare R2,配合 Cache‑Control 設定 30 天快取,首頁載入時間從 4.2 秒降至 1.3 秒,轉換率提升 12%。


總結

  • CDN 為 Three.js 程式庫與大型資源(模型、貼圖)提供全球快速分發,降低首次載入延遲。
  • 雲端平台(Vercel、Netlify、Firebase Hosting)則負責整個靜態網站的部署、CI/CD、以及自動快取機制,讓開發者可以專注在 3D 內容本身。
  • 在實務上,固定版本、處理 CORS、妥善設定快取 是避免相容性問題的關鍵;同時加入 preconnectasync/defer 等最佳實踐,可讓使用者得到更流暢的體驗。

只要掌握上述概念與步驟,即可把你在本機開發的 Three.js 作品,快速、安全且具備全球相容性的上線給全世界的使用者欣賞。祝你部署順利,創造出更多令人驚豔的 Web 3D 體驗!