服务端模板缓存的对象是模板编译后的可执行函数,而非HTML字符串或文档流;它跳过词法分析与AST构建,直接传入数据执行,本质是复用渲染逻辑。

HTML 文档流本身不参与服务端模板缓存,所谓“模板缓存”实际是构建工具或服务端引擎对模板文件(如 .ejs、.njk、.html 片段)的编译结果复用,不是浏览器缓存,也不涉及 HTML 文档流解析阶段。
服务端模板缓存到底缓什么?
服务端模板缓存的对象不是最终生成的 HTML 字符串,而是模板语法解析 + 编译后的可执行函数(比如 EJS 编译出的 function(data) { return ... })。这个函数会被缓存起来,下次遇到相同模板路径时跳过词法分析和 AST 构建,直接传入数据执行。
- Webpack/Vite 的
html-webpack-plugin或vite-plugin-html在开发模式下默认禁用模板缓存(每次变更都重编译),生产构建时会把模板编译结果固化进 bundle,本质是“一次性缓存” - Nunjucks/Express 中启用
env.cache = true后,对同一.njk文件的多次render()调用会复用已编译模板函数,但若模板文件被 fs.watch 监听到变更,缓存自动失效 - CDN 或反向代理(如 Nginx)不会缓存模板编译过程,它们只可能缓存最终输出的 HTML 响应——这属于 HTTP 缓存,和模板缓存无关
为什么不能在构建时缓存整个 HTML 文档流?
HTML 文档流(即浏览器解析 HTML 标签顺序生成 DOM 的过程)是客户端行为,服务端构建阶段根本不存在“文档流”。构建工具输出的是静态字符串,它不携带解析上下文、不触发 DOMContentLoaded、也不执行内联脚本。试图“缓存文档流”等于想缓存一个尚未发生的运行时过程。
- 构建产物是纯文本,哪怕你用
posthtml-include合并了 header.html 和 footer.html,输出仍是扁平字符串,没有 DOM 节点、没有document实例 - 若强行在构建时模拟“流式渲染”(如用 Node.js 的
ReadableStream+TransformStream),不仅无意义,还会破坏 SSR hydration 一致性:客户端 JS 无法匹配服务端输出的“流式结构” - 真正需要流式处理的场景(如大文档分块传输),靠的是 HTTP/1.1 chunked encoding 或 HTTP/2 server push,和模板无关
模板复用失败的三个典型配置陷阱
模板复用效果差,往往不是语法写错,而是缓存策略与构建流程没对齐:
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
立即学习“前端免费学习笔记(深入)”;
- Webpack 使用
html-webpack-plugin时,若template指向的是src/index.html,而该文件里用<!-- htmlhint disable --> <!-- include ./header.html -->,那么./header.html的变更不会触发index.html重新构建——因为 Webpack 默认只监听template文件本身,不递归监听 include 的路径 - Vite 的
vite-plugin-html若开启inject: { data },但data是硬编码对象而非函数,会导致所有页面共用同一份数据快照,无法按路由动态注入 - 使用 Nunjucks 的
{% extends %}时,若父模板base.njk里写了{% block scripts %}<script src="main.js"></script>{% endblock %},子模板却用{% set main_js = 'main.a1b2c3.js' %}但没在 block 内引用,哈希化的资源名根本不会生效
构建阶段模板复用的底线原则
所有模板复用机制,必须满足“一次构建,确定输出”,否则上线后就不可控:
- 禁止在模板中调用
fetch()、require('fs')或任何运行时 I/O —— 构建时无法执行,会导致编译失败或空内容 - 避免用
new Date().toISOString()这类非确定性表达式作为模板变量,否则每次构建输出不同,破坏长期缓存价值 - 如果项目同时用 Web Components(如
<site-header></site-header>)和构建时模板(如posthtml-include),二者不能混用同一份 HTML 片段:前者依赖客户端 JS 加载,后者在构建时已展开,语义冲突
最易被忽略的一点:模板复用的价值不在“少写几行代码”,而在于让变更收敛到最小单元。比如改导航栏,只需动 header.html 并确保所有引用它的页面都能被正确 rebuild —— 这要求构建工具能精确识别依赖图,而不是靠人肉维护 include 列表。


















