从零开始使用 **Taki + Node.js** 实现动态网页转静态网站的完整代码方案

发布于:2025-03-18 ⋅ 阅读:(15) ⋅ 点赞:(0)

以下是从零开始使用 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"

五、技术要点解析

  1. Taki 核心能力

    • 基于 Puppeteer 实现无头浏览器渲染,抓取动态内容139
    • 支持资源过滤(过滤图片/字体)提升生成速度139
  2. Express 优化

    • 静态资源托管 + 缓存控制,提升访问速度4770
    • SPA 路由重定向解决 History 模式 404 问题25
  3. 可靠性设计

    • 失败重试机制应对网络波动23
    • 自动清理旧文件避免冗余34

六、与其他方案对比

方案 适用场景 优势 工具链推荐
Taki 预渲染 SPA/动态内容快速静态化 无需改源码,支持复杂交互 Taki + Express
Next.js SSG 新项目开发 增量生成,开发体验好 Next.js + Vercel
纯静态生成器 内容驱动型站点(博客) 生成速度快,适合 Markdown Hugo/Jekyll

七、常见问题

  1. 图片路径错误

    • 在 Taki 配置中添加资源替换逻辑:
      html = html.replace(/src="\/assets\//g, 'src="assets/');
      
  2. 动态内容更新

    • 结合 Webhook 触发定时重新生成

完整代码参考:Taki 官方文档