以下是從零開始使用 Taki + Node.js 實現動態網頁轉靜態網站的完整代碼方案,包含預渲染、自動化構建、靜態托管及優化功能:
一、環境準備
1. 初始化項目
mkdir static-site && cd static-site
npm init -y
2. 安裝依賴
npm install taki express fs-extra path
二、完整代碼 (generate.js
)
const { request, cleanup } = require('taki');
const express = require('express');
const fs = require('fs-extra');
const path = require('path');// 配置參數
const config = {dynamicSiteUrl: 'http://localhost:3000', // 動態網站本地運行地址outputDir: path.join(__dirname, 'dist'), // 靜態文件輸出目錄routes: ['/', '/about', '/product/:id'], // 需靜態化的路由(支持動態參數)puppeteerOptions: { headless: "new" }, // 使用新版無頭模式resourceFilter: req => !['image', 'font'].includes(req.resourceType()), // 過濾非關鍵資源retries: 3 // 失敗重試次數
};// 1. 生成靜態頁面核心邏輯
async function generateStaticPage(url, outputPath) {let retry = 0;while (retry < config.retries) {try {const html = await request({url,wait: 2000, // 等待頁面渲染puppeteerOptions: config.puppeteerOptions,resourceFilter: config.resourceFilter,htmlSelector: 'body', // 僅抓取body內容(可選)manually: true // 手動觸發快照(等待異步加載)});await fs.outputFile(outputPath, html);console.log(`? 生成成功: ${path.basename(outputPath)}`);return;} catch (err) {retry++;console.error(`? 失敗重試 ${retry}/${config.retries}: ${err.message}`);}}throw new Error(`頁面生成失敗: ${url}`);
}// 2. 批量生成靜態文件
async function generateAllPages() {await fs.emptyDir(config.outputDir); // 清空舊文件for (const route of config.routes) {const dynamicParam = route.match(/:\w+/g)?.[0] || '';const fileName = route.replace(/:\w+/g, '[param]') + '.html';const outputPath = path.join(config.outputDir, fileName);const fullUrl = `${config.dynamicSiteUrl}${route}`;await generateStaticPage(fullUrl, outputPath);}
}// 3. 啟動靜態服務器
function startServer() {const app = express();const port = 3001;// 托管靜態資源(帶緩存優化)app.use(express.static(config.outputDir, {maxAge: '1d',setHeaders: (res) => res.set('Cache-Control', 'public, max-age=86400')}));// 處理SPA路由重定向app.get('*', (req, res) => {res.sendFile(path.join(config.outputDir, 'index.html'));});app.listen(port, () => {console.log(`🚀 靜態服務器運行于 http://localhost:${port}`);});
}// 4. 主流程控制
(async () => {try {await generateAllPages();startServer();} catch (err) {console.error('🔥 嚴重錯誤:', err);process.exit(1);} finally {await cleanup(); // 釋放Puppeteer資源}
})();
三、使用說明
1. 運行動態網站
確保你的 React/Vue 等動態網站在本地 http://localhost:3000
運行。
2. 啟動靜態生成
node generate.js
3. 訪問靜態站點
打開瀏覽器訪問 http://localhost:3001
,所有頁面將以靜態形式呈現。
四、進階功能擴展
1. 動態參數處理(示例)
若路由為 /product/:id
,將生成 /product/[param].html
,Express 會自動匹配如 /product/123
的請求。
2. SEO 優化
在 generateAllPages
函數末尾添加:
// 生成sitemap.xml
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${config.routes.map(route => `<url><loc>https://your-domain.com${route.replace(/:\w+/g, '')}</loc></url>`).join('')}
</urlset>`;
await fs.writeFile(path.join(config.outputDir, 'sitemap.xml'), sitemap);
3. 自動化部署腳本 (deploy.sh
)
#!/bin/bash
node generate.js
tar -czvf dist.tar.gz dist/
scp dist.tar.gz user@server:/var/www/html
ssh user@server "tar -xzvf /var/www/html/dist.tar.gz"
五、技術要點解析
-
Taki 核心能力
- 基于 Puppeteer 實現無頭瀏覽器渲染,抓取動態內容139
- 支持資源過濾(過濾圖片/字體)提升生成速度139
-
Express 優化
- 靜態資源托管 + 緩存控制,提升訪問速度4770
- SPA 路由重定向解決 History 模式 404 問題25
-
可靠性設計
- 失敗重試機制應對網絡波動23
- 自動清理舊文件避免冗余34
六、與其他方案對比
方案 | 適用場景 | 優勢 | 工具鏈推薦 |
---|---|---|---|
Taki 預渲染 | SPA/動態內容快速靜態化 | 無需改源碼,支持復雜交互 | Taki + Express |
Next.js SSG | 新項目開發 | 增量生成,開發體驗好 | Next.js + Vercel |
純靜態生成器 | 內容驅動型站點(博客) | 生成速度快,適合 Markdown | Hugo/Jekyll |
七、常見問題
-
圖片路徑錯誤
- 在 Taki 配置中添加資源替換邏輯:
html = html.replace(/src="\/assets\//g, 'src="assets/');
- 在 Taki 配置中添加資源替換邏輯:
-
動態內容更新
- 結合 Webhook 觸發定時重新生成
完整代碼參考:Taki 官方文檔