You need to enable JavaScript to run this app. React JS运行偶发停止:页面JS失效、时间更新与WebSocket无响应
优惠活动
大模型
产品
解决方案
定价
更多

React JS运行偶发停止:页面JS失效、时间更新与WebSocket无响应

问题核心本质

你观察到的「所有JS逻辑停转、WebSocket消息不接收、点击交互后页面才更新」,是浏览器主线程被长时间完全阻塞的典型表现——不是逻辑本身不运行,是事件循环拿不到执行权,所有待处理的任务(定时器回调、WebSocket消息、用户点击事件)全部被堵在任务队列里等待调度。你贴的两个时间转换函数都是毫秒级的同步计算,本身不会造成卡死问题。

常见诱因

结合你提到的「近期没改核心业务代码、后端服务正常、有时运行30秒就卡死、有时第一次渲染就卡」的特征,诱因基本集中在前端运行时层:

  • 生产环境误打包了React开发版本。开发模式下React会运行大量同步的DOM校验、diff日志逻辑,碰到高频更新、大列表的场景,很容易在几十秒内把主线程占满,且不会抛出明确报错,只会逐步卡到完全无响应;如果列表本身规模很大,甚至第一次渲染完就直接触发卡死,和你描述的表现完全吻合。
  • 定时器未做生命周期清理,逐步触发渲染风暴。很多实现时长更新的逻辑会在每个表格行的渲染流程里单独启动setInterval,组件重渲染、卸载时也不清除旧的定时器,随着列表行数增加、运行时长变长,重复的定时器会越堆越多,每个定时器回调都触发全列表的重渲染,最终渲染次数超出主线程的承受能力,直接卡死。
  • 第三方SDK的同步埋点逻辑卡住主线程。比如全局引入的监控、埋点、在线客服类SDK,最近如果静默更新了版本,碰到高频变化的文本节点(也就是你每秒更新的时长字符串),触发了同步的DOM遍历、数据上报逻辑,就会直接占满主线程。
  • 无memo化的大列表全量重渲染。你现在直接在<td>里调用difference()方法计算时长,没有做任何渲染缓存,只要组件内任意一个无关状态变更,整个列表所有的行都会重新做diff、重新计算时长,行数多了之后很容易把内存占满,触发浏览器的调度降权,表现为逻辑全部暂停。
排查步骤
  • 卡顿复现时打开浏览器开发者工具,切到性能面板录制10秒的执行轨迹,查看火焰图中占满100%线程时间的函数:如果看到React的updateRoot、commitWork类函数占了绝大多数时间,就说明是渲染风暴/开发版React的问题;如果是某个第三方SDK的函数占满时间,就直接定位到对应SDK。
  • 可以在控制台临时执行const id = setInterval(() => {}, 1000),看返回的定时器ID数值,如果数值已经达到几千甚至上万,就说明页面上堆积了大量未清理的重复定时器,属于渲染风暴的前置状态。
  • 在控制台输入React.version确认当前运行的React构建类型,同时查看Network面板加载的React核心JS文件,确认是不是未做代码压缩的开发版本,很多CI流程配置错误时,很容易把开发构建包发布到生产环境。
  • 全局搜索代码库中所有setInterval、setTimeout的调用位置,检查是不是存在组件渲染流程中直接启动定时器、没有在组件卸载时清理的逻辑——尤其是每行单独启动时长更新定时器的写法。
  • 临时注释所有全局引入的第三方SDK,复测是否还会出现卡顿,如果注释后问题消失,就逐一定位是哪个SDK的逻辑引发的阻塞。
对应解决方案
  • 重构时长更新逻辑:不要给每个表格行单独开定时器,用一个顶层的1秒定时任务驱动全局时间戳状态更新,所有行的时长计算都基于这个全局时间戳完成;同时对表格行组件加React.memo包裹,只有行当的item.startsAt或者全局时间戳变化时,才重新计算时长,避免无关状态变更引发全列表重渲染。
  • 所有定时器必须绑定组件生命周期:在useEffect中启动定时器,同时返回对应的清理函数,在组件卸载、依赖项变更时清除旧的定时器,避免重复叠加。
  • 检查生产构建配置,确保打包的是React生产版本,关闭开发模式下的同步校验、日志输出逻辑,降低渲染开销。
  • 大列表场景开启虚拟滚动,只渲染可视区域内的DOM节点,减少一次性的渲染量。

内容的提问来源于stack exchange,提问作者Brandon Kauffman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.06 00:24:02