我构建了 LiveShop,一个迷你直播购物流用户界面,旨在回答我在作为一名对前端充满好奇的毕业生时不断遇到的一个问题:教程教你怎么渲染列表,但从不教你当该列表遭遇真实直播所产生的那种流量冲击时会发生什么。因此,我构建了一个能迫使问题显现的系统,然后修复它,最后测量修复方案是否真正有效。
环境设置
LiveShop 模拟了一场直播购物广播——即小商家在流媒体直播时可能用于销售产品的界面。一个模拟事件引擎按间隔触发聊天消息、互动反应和购买通知,以此替代真实流媒体后端通过 WebSocket 连接所交付的内容。在此基础上,还设有聊天信息流、可滚动的产品轮播组件以及浮动的互动反应动画层。
这些内容本身并不稀奇。有趣的部分始于我提出这样一个问题:当消息量激增时会发生什么?
故障所在
一个初级的聊天信息流实现仅仅是 messages.map(m => <ChatRow key={m.id} {...m} />)。这是任何人都会首先采用的方法,在大多数情况下它运行良好——直到不再良好为止。当有 50 条消息时,看起来没有任何问题。但当消息达到几百条时,每条新消息都会触发文档对象模型(DOM)中每一行的完整重渲染过程,包括那些已经滚动出视图且无人可见的数百行。浏览器正在为不在屏幕上的像素执行布局和绘制工作。
在真实的直播流中,这恰恰是最错误的故障模式,因为消息量的到来并非均匀分布。它会激增——恰好在商品上架后、恰好在直播中出现有趣事情时、恰在一位受欢迎的创作者说出值得引用的话语时。这正是聊天信息流绝不能出现卡顿的时刻,也正是初级实现最可能出现卡顿的时刻。
我的测量结果
与其猜测这是否重要,我构建了一种直接测试它的方法。LiveShop 有一个“模拟激增”按钮,可以瞬间发送 500 条消息,此外还有一个实时帧率(FPS)读数,使用 requestAnimationFrame 来跟踪真实的帧计时——这不是理论估算,而是激增发生时我的机器上帧耗时的实际滚动平均值。
使用普通的 .map() 渲染时,将信息流推至大约 3,700 条累积消息会导致帧率降至 25fps,并伴有明显的卡顿。快速的控制台检查——document.querySelectorAll('.chat-row').length——证实了原因:3,333 个 DOM 节点,对应每一条曾经到达的消息,所有节点仍然挂载并在每次更新时进行差异比对。
修复方案
我使用 react-window 对列表进行了虚拟化处理。它不再为每条消息挂载一个 DOM 节点,而是仅渲染实际位于可见滚动视口内的行——大约同时显示 12–15 行——并在用户滚动时复用这些行。无论信息流容纳 50 条还是 50,000 条消息,DOM 的资源占用都保持不变。
切换方案后,同样的激增测试——数千条消息以模拟流所能生成的最快速度发送——保持了稳定的 60fps,而 DOM 节点数量稳定在 14 个,无论消息计数攀升到多高。
帧率数值是可见的症状。DOM 节点数量是实际的机制,后者更有趣,因为它即使在性能尚未明显退化之前也成立——在激增发生之前,你未必能通过肉眼注意到这一点,但从第一条消息开始,它就存在了。
其他几个承担负载的设
免责声明:本文内容来自互联网,该文观点不代表本站观点。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请到页面底部单击反馈,一经查实,本站将立刻删除。
免责声明:本文内容来自互联网,该文观点不代表本站观点。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请到页面底部单击反馈,一经查实,本站将立刻删除。