排期日历常同时展示日期、票型、库存和价格。拖拽时鼠标每移动一次就更新整个页面状态,容易让大量单元格重复渲染。优化前先确认慢在哪里:浏览器绘制、数据计算、React 提交,还是请求返回。
用 React Profiler 录制一次完整拖拽,重点看每次提交耗时、哪些组件重复渲染、以及拖拽之外的列表是否被带着更新。再用浏览器 Performance 面板对照长任务、布局和绘制时间。仅凭“鼠标移动时卡顿”无法判断一定是 React 的问题。
记录可复现的条件:显示多少天、多少票型、是否打开侧栏、是否有请求,以及设备性能。优化前后使用同一组条件比较,不用一张偶然更快的截图证明效果。
拖拽经过的格子只是临时预览,不一定要每次都更新完整排期数组。即使把指针事件合并为每帧一次,只要每帧调用 setState,React 仍可能重新提交整张日历。一个更直接的做法是:
ref,用 requestAnimationFrame 合并同一帧内的多次事件。transform、宽高和可见性,不触发日历网格的状态更新。这里直接修改 DOM 只限于完全由拖拽逻辑拥有的临时覆盖层,不能让 React 与手工代码同时管理同一组样式。浏览器测试可以用 Profiler 包住日历网格:连续发送指针移动事件,确认矩形在移动,同时网格没有额外提交。这样测到的是优化目标,而不只是“最终选中结果正确”。
memo,并检查传入的对象、数组和回调是否每次都变。简单表达式不必为了“看起来优化”而包进 useMemo。性能优化后检查:快速来回拖动、拖出容器、滚动时继续拖动、窗口失焦、触屏操作,以及组件卸载。预览可以丢帧,最终选择不能丢失;取消拖拽时也不能把临时范围写入正式排期。键盘和触屏入口需要有可用的替代交互。
最后再录制一次相同场景。若提交次数下降但主线程仍被布局或绘制占满,继续给 React 状态加缓存不会解决根因,应回到 DOM 规模和样式计算上排查。