流式渲染必须由服务端显式分块写入并禁用Content-Length,否则浏览器等待完整HTML才解析,TTFB无法低于500ms;Express中需用res.write()+res.flush(),且压缩中间件须前置、flush需Node 18.11+支持、每块HTML须语法闭合。

流式渲染不能靠模板引擎自动开启,必须由服务端显式分块写入并禁用 Content-Length;否则浏览器会等全部 HTML 拼完才开始解析,TTFB 无法低于 500ms。
Express 中 res.write() + res.flush() 的正确调用顺序
Express 默认的 res.send() 会强制缓冲整个响应体、设置 Content-Length 并关闭连接,完全违背流式前提。真正起效的是手动 res.write() 配合 res.flush(),但必须满足三个硬性条件:
- 压缩中间件(如
compression())必须在路由处理函数之前挂载,否则它会把每个write()缓存起来等收全再压 -
res.flush()只在 Node.js 18.11+ 原生支持;旧版本需用res.socket?.write('', () => {})触发底层 flush - 不能在
res.write()后再调res.redirect()或修改状态码,否则抛ERR_HTTP_HEADERS_SENT
典型安全起手写法:
res.write('<!DOCTYPE html><html lang="zh"><head><meta charset="utf-8"></head><body><header>...</header><nav>...</nav>');
res.flush(); // 此刻浏览器已收到可解析的完整片段
骨架 HTML 必须是语法闭合且无阻塞的最小单元
流式不是“发一段缺标签的 HTML 等下一段来补”,浏览器解析器不拼图。每个 res.write() 发出的内容必须是独立可插入 DOM 的闭合结构,否则 Safari 可能卡住、Chrome 可能延迟渲染数秒。
立即学习“前端免费学习笔记(深入)”;
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 允许:
<p>文字</p>、<section><h2>标题</h2> <p>内容</p></section> - 禁止:
<div class="(切在属性值中间)、<div><p>未闭合(标签不配对) - 首屏骨架里不能有同步
<script>、外部<link rel=" stylesheet>,关键 CSS 必须内联且 gzip 后 ≤8KB <li>所有 <code><img alt="HTML标签流式渲染与首字节性能提升教程" >必须带width和height(或aspect-ratio),否则 layout shift 会拖慢 FCP 判定 - Chrome DevTools → Network → 右键表头勾选 “Chunk Size”,观察 document 请求是否出现多行数据块(每块几十到几百字节),而非单行大体积响应
- Performance 面板录制页面加载,检查
Parse HTML是否在First Paint前就已启动,且持续时间短( - 在骨架末尾插入
<script>console.time('skeleton-parsed')</script>,并在完整 DOM 构建后打点,差值若超过 100ms,说明骨架之后仍有大量阻塞逻辑
如何验证流式是否真实生效
别只看 Network 面板里 document 请求的 “Size” 变小了——那可能是 gzip 压缩导致的假象。真正在意的是浏览器是否在收到第一块后立刻开始解析并触发资源加载。
最容易被忽略的点:中文模板文件若带 BOM 头,res.write() 输出的字符串就会乱码;而 charset=utf-8 必须显式写在 <meta> 里,不能只依赖 Node.js 默认编码。


















