当前位置: 首页 > news >正文

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

以下是从零开始使用 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
纯静态生成器内容驱动型站点(博客)生成速度快,适合 MarkdownHugo/Jekyll

七、常见问题

  1. 图片路径错误

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

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

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

相关文章:

  • 谈谈 TypeScript 中的联合类型(union types)和交叉类型(intersection types),它们的应用场景是什么?
  • 代码随想录算法训练营第34天 | 62.不同路径 63. 不同路径 II 整数拆分 不同的二叉搜索树 (跳过)
  • linux(centos8)下编译ffmpeg
  • HCIA-PPP
  • 每天五分钟深度学习PyTorch:循环神经网络RNN的计算以及维度信息
  • 大数据 Spark 技术简介
  • TLSR8355F128芯片特色解析
  • Linux中的epoll简单使用案例
  • 视频转音频, 音频转文字
  • 通过socket实现文件上传和下载功能
  • 信息系统运行管理员教程5--信息系统数据资源维护
  • PAT甲级(Advanced Level) Practice 1023 Have Fun with Numbers
  • LeetCode 1005. K 次取反后最大化的数组和 java题解
  • C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷二)
  • SpringBoot3+Druid+MybatisPlus多数据源支持,通过@DS注解配置Service/Mapper/Entity使用什么数据源
  • Windows11 新机开荒(二)电脑优化设置
  • C++ 类和对象 友元 内部类 this指针 默认成员函数 初始化列表……
  • Pandas DataFrame:数据分析的利器
  • 14 结构体
  • WebSocket和长轮询
  • 多元史料下的“西狩”叙事——《“庚子西狩”中外资料六种》解题
  • ​中国超大规模市场是信心所在——海南自贸港建设一线观察
  • 古埃及展进入百天倒计时,闭幕前168小时不闭馆
  • 梵蒂冈选出新教皇,外交部:望新教皇推动中梵关系不断改善
  • 复旦大学文科杰出教授裘锡圭逝世,享年90岁
  • 两部门部署中小学幼儿园教师招聘工作:吸纳更多高校毕业生从教