核心思路是采用事件驱动、按需解码的流式处理架构:根据数据块首段特征(如SSE标识、JSON片段、压缩魔数、CSV结构)动态匹配并加载对应解码器,解码结果通过EventTarget广播至可插拔的转发路径,同时通过手动流控保障背压传递。
核心思路是:不预先绑定解码器,而是让每一块流入的数据“自己说话”,前端根据其内容特征(如开头标识、结构模式、MIME提示)实时决定用哪个解码对象处理,并将结果推入对应转发路径——整个过程由事件驱动、按需触发、无阻塞等待。
流式数据块到达时,不能靠完整响应体判断类型,需提取首段有效载荷做轻量解析:
JSONStream 或自定义分段 parser)fflate 的 async decompress stream)Papa.parse(chunk, { chunk: true }) 分块解析避免把所有解码器打包进主 bundle,用 dynamic import() 按需加载:
sse-decoder.js、json-chunk-parser.js),导出统一接口 decode(chunk) 和 isMatch(payload)
isMatch(chunk.slice(0, 128)) 判断类型;匹配成功再 import('./decoders/' + type + '-decoder.js')
import() 返回 Promise 的特性,配合 await 实现异步等待加载完成,期间可暂存 chunk 或丢弃(视业务容忍度)解码后数据不是直接渲染,而是进入事件驱动的转发网:
立即学习“前端免费学习笔记(深入)”;
const logSink = new EventTarget()),监听 'data' 事件logSink.dispatchEvent(new CustomEvent('data', { detail: obj }))
logSink.addEventListener('data', renderLogLine)、logSink.addEventListener('data', sendToAnalytics)
前端没有后端那样的背压机制,需手动模拟,防止解码/转发过快撑爆内存:
reader.read() 默认不暂停,需在解码器处理完上一块后再 await reader.read()
requestIdleCallback 或 queueMicrotask 延迟派发,或设置缓冲上限(如最多缓存 5 个未消费事件)pause()/resume() 方法给上层控制流速,例如滚动到底部自动 resume,页面失焦时 pause