C05 HTML 与工程化
摘要:单文件页面在第三个页面出现时开始失控:导航要同步改、页头重复三份、路径散落各处。工程化解决的是「同一份结构只维护一次」。本文对比四种交付方式,给出一个不依赖框架的最小构建脚本,并列出发布前必须核对的六项产物检查。
关键词:模板复用,静态生成,服务端渲染,产物检查,交付
所属:frontend/html / C-高级 | 上一篇:C04-XSS与CSP | 下一篇:D00-总结 | 返回:C00-总结
1 引言
手写 HTML 在页面数量少时效率最高,超过三个页面后维护成本转为线性增长:改一次导航,要改三个文件。工程化的本质是把重复结构抽成数据,再由脚本渲染出最终 HTML。
本文给出四种方案的取舍与一个最小实现。范围见表 1-1。
| 项 | 本文覆盖 | 本文不覆盖 |
|---|---|---|
| 复用 | 模板片段、页面数据、构建脚本 | 组件框架与状态管理 |
| 交付 | 静态托管、静态生成、服务端渲染、客户端渲染 | 容器编排与 CDN 配置 |
| 产物 | 路径、压缩、404 页、缓存头 | 监控与回滚流程 |
2 预备知识
| 项 | 要求 | 核对方式 |
|---|---|---|
| 环境 | Node 20+ | 终端执行 node -v |
| 前置 | A09 | 能独立写完整页面 |
| 前置 | C03 | 了解资源路径与优先级 |
| 目录 | html-practice/build/ 含 pages/ 与 partials/ |
新建即可 |
3 原理与实现
3.1 从源文件到产物
构建只做三件事:读数据、套模板、写文件,如图 3-1 所示。
flowchart LR
A["页面数据<br/>pages/*.json"] --> C["构建脚本"]
B["模板片段<br/>partials/*.html"] --> C
C --> D["渲染完整 HTML"]
D --> E["dist/ 产物目录"]
E --> F["静态托管<br/>nginx / 对象存储"]
E --> G["本地打开校验"]
图 3-1 模板复用的构建流程
图上可见:产物是完全静态的 HTML,运行时不依赖构建脚本;这决定了它可以在任何静态托管上运行。
3.2 最小构建脚本
// 运行环境:Node 20+;保存为 build/build.mjs,执行 node build/build.mjs
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
const root = new URL('.', import.meta.url).pathname;
const layout = readFileSync(join(root, 'partials/layout.html'), 'utf8');
const pages = [
{ slug: 'index', title: '首页', body: '<h1>首页</h1><p>站点简介。</p>' },
{ slug: 'about', title: '关于', body: '<h1>关于</h1><p>作者与联系方式。</p>' },
];
for (const page of pages) {
const html = layout
.replaceAll('{{title}}', page.title)
.replaceAll('{{body}}', page.body);
mkdirSync(join(root, 'dist'), { recursive: true });
writeFileSync(join(root, 'dist', `${page.slug}.html`), html, 'utf8');
console.log(`已生成 dist/${page.slug}.html`);
}
<!-- partials/layout.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{title}} | 李明的工作笔记</title>
<link rel="stylesheet" href="./assets/main.css" />
</head>
<body>
<header><nav><a href="./index.html">首页</a> <a href="./about.html">关于</a></nav></header>
<main>{{body}}</main>
<footer><p>© 2026 李明</p></footer>
</body>
</html>
四种交付方式的取舍见表 3-1。
| 方式 | 首屏内容来源 | 适用场景 | 代价 |
|---|---|---|---|
| 手写静态页 | 构建期已固定 | 3 页以内 | 重复结构,改一处改多处 |
| 静态生成 | 构建期渲染成 HTML | 内容型站点、文档站 | 需要构建流程与产物管理 |
| 服务端渲染 | 每次请求生成 | 需登录态、个性化内容 | 需服务器与运行时 |
| 客户端渲染 | 浏览器中生成 | 强交互应用 | 首屏空壳,检索与分享不友好 |
发布前必须核对的六项见表 3-2。
| 序号 | 检查项 | 判定标准 |
|---|---|---|
| 1 | 资源路径 | 产物中无 ../ 越界,图片可访问 |
| 2 | 文档结构 | 每页保留 doctype、lang、charset、viewport |
| 3 | 链接 | 站内链接全部可达,无 # 占位 |
| 4 | 占位符 | 产物中无 {{...}} 残留 |
| 5 | 404 页 | 存在 404.html,且样式与站点一致 |
| 6 | 体积 | 单个 HTML 未超预期,图片已压缩 |
4 验证与讨论
- 建立
partials/layout.html与build/build.mjs,执行脚本,确认生成两个页面。 - 打开
dist/index.html,确认导航、页脚、标题均来自模板,正文来自数据。 - 在产物目录搜索
{{,确认无残留占位符。 - 修改模板中的导航,重新构建,确认两个页面同步更新。
| 检查项 | 预期结果 | 说明 |
|---|---|---|
| 产物完整 | 两个 HTML 文件生成 | 脚本遍历数据 |
| 结构一致 | 两页头部与页脚完全相同 | 模板复用 |
| 无占位符残留 | 搜索 {{ 为 0 命中 |
替换逻辑覆盖全部键 |
| 本地可打开 | 双击产物正常渲染,无构建依赖 | 纯静态 |
| 路径可用 | 图片与样式在 dist/ 内可访问 |
资源需同步复制到产物 |
讨论与局限。 字符串替换模板无法处理循环与条件,页面数量继续增长时应引入模板引擎或静态站点生成器。另构建产物需要连同 assets/ 一起发布,仅复制 HTML 会导致样式与图片全部 404。
5 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 产物打开后无样式 | assets/ 未复制到 dist/ |
构建脚本增加复制步骤 |
| 占位符原样出现在页面上 | 数据字段名拼写与模板不一致 | 统一键名,构建后搜索 {{ 核验 |
| 站内链接 404 | 产物路径与链接写法不一致 | 统一相对路径,按 R22 校验 |
| 中文乱码 | 写入时未指定 UTF-8 | writeFileSync 显式指定编码 |
| 每次构建都要手工清理旧文件 | 未清理产物目录 | 构建前删除 dist/,避免残留旧页 |
6 结论
| 序号 | 结论 | 依据 |
|---|---|---|
| 1 | 工程化解决「一份结构只维护一次」 | 图 3-1 |
| 2 | 静态生成适合内容型站点,交付成本最低 | 表 3-1 |
| 3 | 发布前六项检查缺一不可 | 表 3-2 |
| 4 | 产物必须自带资源,不能依赖构建环境 | 4 节步骤 4 |
- 上一篇:C04-XSS与CSP
- 下一篇:D00-总结
- 返回:C00-总结 | 科目 README
参考文献
[1] MDN Web Docs · 发布网站,https://developer.mozilla.org/zh-CN/docs/Learn/Getting_started_with_the_web/Publishing_your_website(访问于 2026-10-29)
[2] Node.js 文档 · fs 模块,https://nodejs.org/api/fs.html(访问于 2026-10-29)

评论 0
还没有评论,快来抢沙发吧~