以下是从零开始使用 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 核心能力
Express 优化
可靠性设计
六、与其他方案对比
方案 | 适用场景 | 优势 | 工具链推荐 |
---|---|---|---|
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 官方文档