HTML解析器在词法分析阶段将所有空白字符统一转为标准空格,再将连续空白压缩为单个空格,该操作仅作用于文本节点,不处理标签间空白、属性值及<pre><textarea><script><style>等需保留空白的上下文。

HTML解析器怎么合并连续空白字符
HTML解析器在词法分析阶段就把所有空白字符(U+0020空格、U+0009制表符、U+000A换行、U+000D回车、U+000C换页)统一转成标准空格,再把连续出现的多个空白压缩为单个U+0020。这个过程不依赖CSS或DOM树构建,发生在HTML文本转为token之前。
关键点是:它只对“文本节点中的空白”做折叠,不碰标签之间空白的语义影响(比如AB里那个空格会被保留用于渲染间隙);也不处理<pre>、<textarea>、<script>、<style>等显式要求保留空白的上下文。
- 文本节点内:
"a\n\t b"→ 渲染为"a b" - 标签之间:
<span>a</span>\n<span>b</span>→ DOM中两个span相邻,中间换行被忽略,但不会插入空格 - 属性值中:
<div title=" a b ">→title属性值仍为" a b ",浏览器不压缩属性值里的空白
哪些空白可以安全删,哪些必须留
源码中能删的空白,和浏览器是否渲染它,是两回事。真正影响体积的是传输字节数,不是渲染效果。
可安全删除:
立即学习“前端免费学习笔记(深入)”;
- 页面级换行与缩进,如
<div>\n <p>xxx</p>\n</div>中的换行和空格 - 标签之间的空白(只要不产生语义空格),例如
<div><p>1</p><p>2</p></div>中间无空白,比换行+缩进省几百字节 -
<meta charset="utf-8">这种自闭合标签前后的换行
不能删(否则破坏语义或功能):
-
<pre>、<textarea>、<script>、<style>内部的所有空白和换行 - 文本节点中本就具语义的空格,如地址字段
"北京市 朝阳区"里的或空格分隔 -
<img src="x">文字紧挨着写会丢失间隙,需用<img><!-- -->文字或CSS控制
html-minifier-terser 的 collapseWhitespace: true 到底干了什么
它不是模拟浏览器行为,而是字符串层面粗暴替换:把所有非标签、非属性值区域的空白序列(包括注释前后、标签之间、文本节点内)全替换成单个空格。
这带来几个实际风险:
- 误删
<code>for (let i = 0; i < n; i++)</code>里本该保留的空格,导致代码格式错乱 - 把
<div>A</div>\n<div>B</div>变成<div>A</div><div>B</div>,若CSS依赖兄弟元素间默认间距,布局可能塌陷 - 不识别
white-space: pre-wrap等声明,也不会跳过<template>内的服务端模板占位符
推荐配置组合:
-
collapseWhitespace: true(必须) -
ignoreCustomFragments: [/<code>[\s\S]*?<\/code>/, /<pre>[\s\S]*?<\/pre>/](手动保白名单) -
removeComments: true,但设ignoreCustomComments: [/^!build:/]留构建标记 -
removeRedundantAttributes: true(安全),但removeEmptyAttributes: false(避免删掉class=""这类JS查询依赖)
为什么 inline CSS/JS 不该靠 HTML 压缩来省体积
把<style>或<script>写在HTML里,即使collapseWhitespace能压掉几KB,也掩盖了一个更严重的问题:这些资源无法被CDN缓存、无法复用、无法并行加载。
实测对比:
- 10KB内联JS + HTML压缩后体积减800B → 首屏TTFB不变,但每次请求都重传JS
- 外链
<script src="main.js"></script>→ JS可长期缓存,HTML体积直降10KB,HTTP/2下还能复用连接
真正拖慢加载的,从来不是HTML里多出的2KB换行,而是未压缩的<script>里混着100行调试console、SSR模板每层循环都输出4空格缩进、或者用几十个 强行对齐——这些才是该优先清理的。
压缩HTML源码只是下游优化,上游结构不合理时,再干净的空格也救不了性能。



















