模板文件解析阶段必须预缓存,否则每次请求都重解析;正确做法是启动时一次性编译并缓存模板函数,避免高并发下cpu打满和响应延迟飙升。

模板文件解析阶段必须预缓存,否则每次请求都重解析
Node.js 服务中,fs.readFileSync 读取模板再调用 compile 是最常见错误——它让 V8 每次都重新词法分析、生成 AST、编译函数。高并发下 CPU 直接打满,响应延迟飙升。
正确做法是启动时一次性解析并缓存编译后的模板函数:
- 使用
template.compile()(如 EJS)或handlebars.compile()得到可复用的函数,存入内存 Map 或全局对象 - 避免在
res.render()内部重复调用template.compile();若用 Express + EJS,默认已做缓存,但需确认cache: true已启用(默认开启) - Pug 和 Handlebars 同样支持预编译:Pug 可用
pug.compileFile()提前加载,Handlebars 推荐用Handlebars.precompile()输出 JS 函数再require
大页面要分片输出,别等全部数据就绪才 res.end()
用户看到首屏内容的时间,取决于服务器吐出第一个字节(TTFB)和首屏 HTML 到达浏览器的时间。如果整个页面(含侧边栏、评论区、广告位)全靠一次 res.write() + res.end(),首屏会卡住直到所有数据 fetch 完。
可行的分片策略:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 把 HTML 拆成逻辑块:
<header></header>、<main></main>、<aside></aside>,每块独立获取数据、独立渲染、独立res.write() - 用 Node.js 的
stream或Writable接口,把模板渲染结果以 chunk 方式写入响应流,配合res.flushHeaders()强制发送响应头 - 注意:Express 默认不支持流式写入,需手动设置
res.socket.setNoDelay(true)并禁用res.send(),改用res.write()+res.end()
ignoreCustomFragments 配置错会导致压缩后模板语法失效
上线前用 html-minifier 压缩 HTML 是常规操作,但模板语法(如 {{user.name}}、)若未被正确忽略,会被删掉或转义,导致服务端渲染白屏或报错 ReferenceError: user is not defined。
关键配置项必须显式声明:
- EJS 项目:确保
ignoreCustomFragments包含// - Handlebars 项目:加
/{{[\s\S]*?}}/和/{%[\s\S]*?%}/(用于 partial 或 block helper) - Go 的
html/template不需要此配置,因其本身不走第三方 minifier,但若用外部工具压缩,需保护{{.Name}}类语法 - 检查方法:压缩后打开 HTML 源码,搜索
{{或,确认它们原样保留,没被删减或变成 <code>{{
缓存策略不能只看模板,要区分静态资源与动态 HTML
很多人把 Cache-Control: public, max-age=3600 直接套在所有 HTML 响应上,结果首页 banner 更新了,用户刷新还是旧版——因为 CDN 或浏览器缓存了整页 HTML。
真实场景中必须分层控制:
-
index.html或路由页(如/article/123)必须设为no-cache或max-age=0, must-revalidate,强制回源校验 - 带哈希的静态资源(
app.a1b2c3.js、style.f4e5d6.css)可设public, immutable, max-age=31536000 - CDN 缓存 HTML 是高危操作:一旦模板逻辑改错或数据接口异常,所有边缘节点同步返回错误 HTML,且无法秒级回滚
- 更稳妥的做法:HTML 不缓存,但把高频不变区块(如导航栏、页脚)抽成独立 API,前端用
fetch()加Cache-Control: max-age=300缓存,兼顾速度与可控性
ignoreCustomFragments 配置验证——上线前不看源码,只测功能,压缩后模板语法消失的问题往往要等用户反馈才暴露。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










