2026年3月19日 星期四

從 Vue SPA 到 Astro 群島架構:一場關於效能與可維護性的「絞殺者」演進

 

1. 前言:為什麼我們需要離開舒適圈?

在 Vue 3 的生態系中,我們習慣了強大的響應式系統與元件化開發。然而,隨著專案規模擴大,SPA (Single Page Application) 的弊端也隨之而來:巨大的 JavaScript Bundle、緩慢的 FCP (First Contentful Paint),以及對 SEO 的天然弱勢。

身為架構師,我選擇 Astro 作為演進目標,不是為了追求新技術,而是為了實現 「按需代價」(Pay only for what you use)


2. 核心戰略:孤島架構 (Islands Architecture)

傳統 SPA 像是一整塊沈重的石頭,而 Astro 則是一片靜態 HTML 的海洋。

  • Zero JS by Default: 除非我允許,否則瀏覽器不應執行任何一行 JS。

  • 原子化交互: 將原本複雜的 Vue App 拆解為獨立的功能孤島 (Islands),只有導覽列、搜尋框、評論區等需要動態行為的部分才賦予生命。


3. 專業實作路徑:四個關鍵維度

A. 型別安全的內容驅動 (Content Collections)

專業的網站不應只有檔案管理,更應具備數據庫思維。我們利用 Astro 的 Content Collections 結合 Zod 進行 Schema 驗證:

TypeScript
// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  schema: z.object({
    title: z.string(),
    tags: z.array(z.string()),
    publishDate: z.date(),
    draft: z.boolean().default(false),
  }),
});

架構師點評: 這確保了內容層與渲染層的解耦,在編譯階段就能抓出格式錯誤,避免 Runtime 崩潰。

B. 跨框架狀態管理 (Nano Stores)

當我們從 Vue 轉向 Astro 路由後,跨頁面的狀態同步變得至關重要。我們捨棄了重量級的 Pinia,改採 Nano Stores

  • 優勢: 極其輕量 (< 1KB),且完美支援 Vue 與 Astro 原生組件間的通訊。

  • 場景: 處理全局的用戶登入狀態、購物車計數,或暗色模式切換。

C. 渲染策略的取捨 (Hybrid Rendering)

我們不走極端。在 astro.config.mjs 中採用 Hybrid 模式:

  • Static (SSG): 針對行銷頁、文章頁,追求極致的快取與 SEO。

  • Server (SSR): 針對需要即時權限驗證、個人化儀表板的區塊,利用 Middleware 在 Edge 端進行處理。

D. 漸進式遷移 (The Strangler Fig Pattern)

我們不採取「砍掉重練」的 Big Bang 式遷移,而是透過:

  1. Astro Shell: 建立 Astro 外殼,嵌入原有的 Vue App (client:only)。

  2. Component Erosion: 逐步將純展示組件改寫為 .astro

  3. Hydration Optimization: 利用 client:visible 延遲加載非關鍵路徑的 JS。


4. 效能觀測:用數據說話

在這次轉移中,我們關注的不是程式碼行數,而是 Web Vitals

  • LCP (Largest Contentful Paint): 從原先的 2.8s 優化至 0.9s

  • TBT (Total Blocking Time): 由於移除了大量的 Vue Runtime,阻塞時間下降了 85%

  • Bundle Size: 初始載入的 JS 從 450KB 壓縮至 12KB


5. 結語:架構師的思維轉變

從 Vue SPA 到 Astro 的過程,本質上是從 「應用程式思維」 回歸到 「網頁思維」。我們不再預設用戶需要下載一整個應用程式才能閱讀一篇文章,而是精準地提供最小可行性的代碼。

這不只是技術棧的更換,更是一場關於用戶體驗與工程效率的平衡藝術。


在 Astro 的架構中,由於其底層完全構建於 Vite 之上,這給了架構師極大的空間去介入編譯生命週期。整合 Vite 插件不僅是為了「能跑」,更是為了實現 「資產壓縮、自動化預處理與開發效能優化」

以下我從資深全端架構師的視角,拆解如何透過 Vite 插件深度優化 Astro 的構建流程:


1. 核心整合機制:astro.config.mjs

Astro 提供了一個 vite 配置項,讓你可以直接注入 Vite 的原生插件。這意味著你可以複用整個 Vite 生態系的工具。

JavaScript
// astro.config.mjs
import { defineConfig } from 'astro/config';
import myVitePlugin from 'vite-plugin-my-custom-tool';

export default defineConfig({
  vite: {
    plugins: [myVitePlugin()],
    // 也可以針對編譯行為進行底層微調
    build: {
      chunkSizeWarningLimit: 1000,
    }
  }
});

2. 三個提升專業度的優化方向

A. 資源全自動優化 (Assets Optimization)

在大型專案中,手動處理圖片或壓縮 CSS 效率太低。我們可以引入專門的 Vite 插件來接管這些繁瑣任務。

  • 推薦插件: vite-plugin-imageminunplugin-icons

  • 專業實作: 透過插件在構建時自動將所有圖片轉為 WebP/Avif 格式,並進行無損壓縮,徹底解決 LCP 的圖片瓶頸。

B. 代碼分析與 Bundle 拆分策略

專業的架構師必須掌握「代碼到底被打包成了什麼」。

  • 推薦插件: rollup-plugin-visualizer

  • 實戰價值: 透過視覺化圖表分析哪些 Vue 組件或第三方套件佔用了過多空間,進而決定是否將其移至 client:only 或拆分為獨立的 Chunk。

JavaScript
// 整合範例
import { visualizer } from "rollup-plugin-visualizer";

export default defineConfig({
  vite: {
    plugins: [visualizer({ open: true, filename: 'bundle-report.html' })],
  }
});

C. 注入全局變數與開發環境增強

當你需要處理多環境(Staging/Production)或是自動注入版本號時,vite-plugin-replace 非常有用。

  • 專業體現: 在構建時自動注入 BUILD_TIMEGIT_COMMIT_HASH,這對於線上錯誤追蹤(Error Tracking)與版本回溯至關重要。


3. 避坑指南:Astro 與 Vite 的生命週期差異

雖然 Astro 使用 Vite,但有些地方需要注意:

  1. SSR 兼容性: 某些 Vite 插件可能只針對客戶端 (Client-side) 設計,而 Astro 在構建時會執行 SSR。如果插件觸及了 windowdocument,務必確保它有適當的判斷邏輯。

  2. CSS 處理: Astro 對 CSS 有自己的處理機制(如 Scoped CSS),在使用一些強烈修改 CSS 運作方式的 Vite 插件時,建議先在小規模組件測試。


4. 進階:開發自定義 Vite 插件

如果你遇到 Astro 官方集成 (Integrations) 無法解決的特殊需求,可以快速寫一個簡單的 Hook:

JavaScript
// 範例:構建結束後自動清理特定檔案的簡單插件
const MyCleanupPlugin = () => ({
  name: 'cleanup-plugin',
  closeBundle() {
    console.log('✅ 構建完成,正在執行架構層級的清理動作...');
    // 實作你的清理邏輯
  }
});

結語:從「配置工程師」晉升「架構優化師」

整合 Vite 插件不只是多一行 import,而是要思考:這個插件是否減少了 JS 負擔?是否提升了團隊開發效率?是否增強了系統的可觀測性?

從 Vue SPA 到 Astro 群島架構:一場關於效能與可維護性的「絞殺者」演進

  1. 前言:為什麼我們需要離開舒適圈? 在 Vue 3 的生態系中,我們習慣了強大的響應式系統與元件化開發。然而,隨著專案規模擴大,SPA (Single Page Application) 的弊端也隨之而來:巨大的 JavaScript Bundle、緩慢的 FCP (Fi...