“读写分离”是浏览器渲染优化原则:先集中读取布局信息,再批量修改样式,避免强制回流。每帧混用读写会多次触发回流,耗尽16.6ms;应缓存几何数据,用transform+will-change更新,并及时清理离屏元素。
在 60FPS 游戏循环中,“读写分离”不是指数据库层面的主从架构,而是针对浏览器渲染机制提出的一种 DOM 操作组织原则:把所有“读取布局信息”的操作集中执行(读阶段),再把所有“修改样式或结构”的操作批量执行(写阶段),从而避免触发多次强制回流。
每次读取 offsetTop、getBoundingClientRect()、clientWidth 等属性时,浏览器必须立刻 flush 队列、执行一次完整回流,以返回准确值。如果在动画循环中边读边写——比如每帧先算位置、再改 left、再算碰撞、再改 transform——就等于每帧强制触发多次回流,16.6ms 很快耗尽。
以 requestAnimationFrame 驱动的主循环为例,把一帧拆成明确的两个阶段:
getBoundingClientRect() 或缓存所有动态元素的 offsetLeft/offsetTop,把所需几何数据全部读出并暂存到 JS 对象中transform: translateX(x) translateY(y) 批量更新所有元素样式,不读、不查、不判断仅分离读写还不够,需配套以下做法才能稳住 60FPS:
transform + will-change: transform,避免触发布局(layout)和绘制(paint)全通路removeChild 并清空数组引用,不靠 display:none 或 visibility:hidden 占着内存和渲染管线left/top 或 width/height 动态修改,这些属性会直接引发回流错误写法(每帧 3 次强制回流):
ball.style.left = ball.offsetLeft + 2 + 'px';正确写法(0 次强制回流):
const ballRect = ball.getBoundingClientRect();