SSG
SSG 在建置時就把每條路徑產出 HTML,速度快、成本低、可索引性高,是內容型網站的強力選項。
定義
SSG(Static Site Generation)在 build 時把頁面渲染成靜態 HTML 檔案。部署後不需要每次請求都跑伺服器渲染,通常在 CDN 上就能極快回應,非常適合教學、部落格、詞彙表與工具介紹等內容頁。
為什麼重要
- 效能最好:CDN 直接回傳 HTML,TTFB 極低(通常 < 50ms)
- SEO 友善:每路徑都有可索引 HTML,不依賴 JavaScript 執行
- 部署簡單:靜態站點非常適合 Cloudflare Pages、Vercel、Netlify
- 成本最低:不需要伺服器運算,只有 CDN 流量費用
- 可靠性高:沒有伺服器故障風險,CDN 有多層冗餘
- 支援增量更新:ISR(Next.js)或 on-demand rebuild 可局部更新
- 利於 AI 引擎索引:ChatGPT/Perplexity 的爬蟲渲染能力有限,靜態 HTML 最可靠
怎麼做(實作重點)
- 把可靜態化的頁面在 build 期 prerender(如詞彙表、教學、工具頁)
- 確保每頁 meta/canonical/hreflang/schema 都是 route-aware
- 內容更新後重新 build/deploy,並更新 sitemap
- 使用 ISR 或 webhook 觸發增量建置,減少完整 rebuild 時間
- 設定 CDN 快取策略:immutable for hashed assets, s-maxage for HTML
- 建立 prerender 路由清單:從 CMS 或 JSON 自動產生需 prerender 的路徑
- 驗證 SSG 輸出:確保每個 HTML 檔案都有完整 head 與結構化資料
範例
typescript
// scripts/prerender.ts
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom/server';
import fs from 'fs';
import path from 'path';
import App from '../src/App';
import glossary from '../src/content/glossary.json';
// 從 JSON 產生路由清單
const routes = [
'/',
'/glossary',
...glossary.terms.map(t => `/glossary/${t.slug}`),
...glossary.terms.map(t => `/en/glossary/${t.slug}`),
];
const template = fs.readFileSync('dist/index.html', 'utf-8');
for (const route of routes) {
const html = renderToString(
<StaticRouter location={route}><App /></StaticRouter>
);
const output = template.replace('<!--ssr-outlet-->', html);
const dir = `dist${route}`;
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, 'index.html'), output);
console.log(`Prerendered: ${route}`);
}typescript
// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://developer-seo-hub.com',
integrations: [
react(),
sitemap(), // 自動產生 sitemap.xml
],
output: 'static', // 純 SSG 模式
build: {
// 為 hashed assets 設定長期快取
assets: '_astro',
},
});
// src/pages/glossary/[slug].astro
---
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const terms = await getCollection('glossary');
return terms.map(term => ({
params: { slug: term.slug },
props: { term },
}));
}
const { term } = Astro.props;
---
<html><head><title>{term.data.title}</title></head>...</html>相關連結
常見問題
關於這個詞彙的常見問答。