外观
渲染卡顿怎么处理?
先让主线程少做布局和绘制,再减少不必要的组件更新。动画用 transform 和 opacity。长列表只渲染可见项。读布局和写样式要分开批处理,避免强制同步布局。
原理
强制同步布局发生在同一段脚本里交错读取几何信息(offsetWidth、getBoundingClientRect)和修改样式。浏览器为了给你最新数值,不得不提前把布局做完。正确的顺序是:先把要读的都读完,再一起写。
能不触发布局的动画就不要改 top、width、margin。transform 和 opacity 通常留在合成阶段,由 GPU 完成。will-change 只加在确实要独立成层、且数量很少的元素上,层太多会占内存。
长列表用虚拟列表,DOM 数量跟视口走,不跟数据总量走。见 无限列表。
React 里减少渲染:
- 状态下放到真正用它的组件,避免抬得太高。
- 列表项用稳定
key,并用memo挡住父组件带来的无效更新。 - Context 按变化频率拆分。见 状态管理。
- 输入用
startTransition把非紧急的列表刷新标成可打断。
content-visibility: auto 让屏幕外的大块内容跳过布局和绘制。确认可访问性和滚动条高度还能接受,再使用。
追问
60fps 对应多少时间?
一帧大约 16.7ms。这是预算,不是定时器间隔。一帧里要完成脚本、样式、布局、绘制和合成。超过就会掉帧。用性能面板看长任务,不要用 setTimeout(0) 去猜。
memo 是不是加得越多越好?
比较 props 本身有成本。子树很便宜时,浅比较可能更亏。先用 Profiler 找到渲染次数多、渲染又贵的节点。
易错点
- 卡顿不一定是「Vue/React 慢」。主线程上的长任务、大图片解码、同步的
localStorage都会卡住输入。 - 在渲染函数里创建新对象,再交给依赖它的子组件,
memo会失效。 - 测量时开着很多扩展,结果不能代表用户。