以下是从零开始使用 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 官方文档