必须用 TextDecoderStream 将 fetch().body 的 Uint8Array 流解码为字符串流,再交由 htmlparser2 流式解析;DOMParser 不支持流式处理,手动 decode 易出错;需注意响应头、编码、背压及流生命周期管理。

fetch().body 是 Uint8Array 流,不是 HTML 字符串流
直接把 response.body 传给 DOMParser 或 innerHTML 会失败,因为它是原始字节流,不是 UTF-8 解码后的字符串。浏览器不会自动解码,中文、emoji、特殊符号全变乱码或解析中断。
- 必须用
response.body.pipeThrough(new TextDecoderStream())转成字符串流;TextDecoderStream是浏览器原生支持的流式解码器,能正确处理跨 chunk 的多字节字符(比如一个中文被切在两个 Uint8Array 中间) - 别用
new TextDecoder().decode(chunk)手动解码——它不支持{ stream: true },遇到截断字符会抛错或丢字 - 如果服务端响应头没设
Content-Type: text/html; charset=utf-8,TextDecoderStream默认仍按 utf-8 解码,一般够用;但若服务端用 gb2312 或 utf-16,得显式传参new TextDecoderStream('gb2312')
htmlparser2 比 DOMParser 更适合流式 HTML 处理
DOMParser 必须等整个 HTML 字符串加载完才能 parse,一卡全卡;而 htmlparser2 的 Parser 构造函数天生支持流式写入,靠事件驱动,边收边吐。
- 初始化时设
xmlMode: false(默认即可)、decodeEntities: true,避免实体如被忽略 - 每收到一个字符串 chunk,调用
parser.write(chunk);parser内部会缓存未闭合标签(比如跨 chunk),等后续数据到来再继续解析,不崩溃- 不要在流结束前调用
parser.end(),否则会强制 flush 并报 “Unexpected end” 错误;应在reader.read()返回{ done: true }后再调用- 匹配
<script></script>或提取href时,别依赖querySelectorAll——DOM 还没建完;改用onopentag+ontext回调捕获目标节点想边读边改 HTML(如重写链接),得自己写 TransformStream
浏览器没有
HTMLStream这个标准 API,你在文档里看到的只是 EdgeRoutine 等私有平台的封装。普通网页必须手动组合TextDecoderStream→TransformStream→ 自定义处理器。-
TransformStream的transform()方法接收 chunk 字符串,你可以用正则替换/href="([^"]*)"/g,但注意:属性值可能含换行、转义引号(如href="xxx"),简单正则容易漏 - 更稳的做法是把
TransformStream的 readable 端接给htmlparser2,在onopentag里判断 tagName === 'a' 且 attrs.href 存在,再改写后用this.write(...)输出新标签 - 输出必须是合法 HTML 片段(不能只输出 href 属性值),否则下游消费方(比如
WritableStream写入文件或document.write)会出错 - 别忘了设置
desiredSize或用controller.enqueue()控制背压,否则高速流可能撑爆内存
流式处理失败最常见的三个坑
不是代码写错,而是对流生命周期和浏览器限制理解不到位。
- 服务端没开分块传输(
Transfer-Encoding: chunked)或返回了Content-Length且值固定——这种响应体浏览器仍会当成“可流”,但实际是等全部发完才触发body可读,失去流式意义 - 在主线程里用同步循环(如
while(!done) { await reader.read() })没加await,导致 Promise 未 resolve 就进入下一轮,value为undefined,解码时报错 - 没监听
abort信号或没调用reader.cancel(),用户点取消后流还在后台跑,内存不释放,甚至触发重复回调
真正难的不是写几行
pipeThrough,而是判断哪一层该缓存状态、哪一层该丢弃引用、哪个错误该重试、哪个该静默吞掉——这些没日志、没调试器,只能靠 chunk 边界和回调触发节奏反推。 - 不要在流结束前调用
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











