浏览器原生 readablestream 读 html 易因编码、分块边界、标签截断出错;需用 textdecoderstream 解码,htmlparser2 流式解析更稳,domparser 需完整字符串;htmlstream 非标准 api,普通网页须自建 transformstream 实现流式处理。

浏览器原生支持 ReadableStream,但直接用它读取 HTML 字符串或文件内容时,容易卡在编码、分块边界、标签截断这三关——不是乱码,就是解析器崩溃。
如何用 fetch().body 正确拿到 HTML 流
很多开发者以为 fetch() 返回的 response.body 拿来就能当 HTML 流用,其实它只是原始字节流(Uint8Array),没做 UTF-8 解码,也没按 HTML 标签切分。
- 必须用
response.body.pipeThrough(new TextDecoderStream())转成字符串流,否则中文会变 - 不能直接传给
DOMParser或htmlparser2,它们不接受流,只接受完整字符串或ReadableStream+ 自定义 parser - 若目标是边收边改(如边缘重写
<a></a>标签),得用TransformStream套一层处理逻辑,而不是“读完再处理”
为什么 htmlparser2 配合 stream 更稳
htmlparser2 的 Parser 构造函数支持 xmlMode: false 和 decodeEntities: true,且内部用 write() 接收 chunk,天然适配流式输入;而 DOMParser 必须等整个字符串就绪,一卡全卡。
- 每收到一个
Uint8Arraychunk,先decoder.decode(chunk, {stream: true})得到字符串片段 - 把该字符串喂给
parser.write(),触发onopentag/ontext等回调 - 遇到
<script></script>或未闭合标签时,htmlparser2会缓存状态继续等后续 chunk,不报错 - 别用
parser.end()除非确认流已结束;实际中应监听response.body.getReader().read()的done === true
HTMLStream 不是标准 API,别在普通网页里硬套
你看到的 HTMLStream 是 EdgeRoutine(ER)平台的私有扩展,依赖服务端运行时环境。它底层封装了 TransformStream + CSS 选择器匹配引擎,但在 Chrome/Firefox 中直接 new HTMLStream() 会报 ReferenceError。
- 普通前端项目想实现类似功能,得自己写
TransformStream:输入是TextDecoderStream输出,输出是加工后的字符串流 - 选择器匹配不能靠
querySelectorAll(DOM 未构建完),得用正则或htmlparser2的事件驱动方式捕获目标节点 - 如果只是替换所有
href,用replace(/href="([^"]*)"/g, 'href="https://example.com"')更快,但注意属性值可能跨行或含转义
大 HTML 文件本地读取时的分块陷阱
用 input[type=file] 选一个 50MB 的 HTML 文件,file.slice() 分块读取时,若按字节切(比如每 1MB),很可能把一个 <div>...</div> 从中间劈开,导致 htmlparser2 收到不完整标签而丢数据。
- 不要按固定字节数切,改用
FileReader+onload逐块读,每次从上一块末尾的最后一个>后开始下一块 - 更稳妥的做法:先用
file.text()读小文件(Web Worker 把slice+text()操作移出主线程 -
performance.now()监控单块读取耗时,超过 100ms 就降级为 512KB 块大小,避免 UI 卡顿
真正难的不是“怎么流”,而是“在哪断、怎么续、断了之后状态怎么保持”——这些细节不写进 parser 状态机里,光靠 ReadableStream 接口本身解决不了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











