paint flashing 高亮显示的是实际重绘区域与频率,而非提示动画存在;grid 中 hover 图片导致整行闪烁,主因是图层未合理分离,需通过 translatez(0) 等触发独立合成层,避免 relative、overflow:hidden 与 transform 组合干扰图层提升。

Chrome DevTools 中 Paint Flashing 显示大面积高亮
Paint Flashing(在 Rendering 面板中开启)不是“提示你有动画”,而是直接告诉你:哪些区域被重绘了、重绘多频繁。如果 Grid 容器里 hover 一个 <img>,结果整行甚至整个容器都持续闪黄/绿,说明重绘范围远超预期——这不是动画写错了,是图层没分好。
常见误判:以为加了 transform: scale() 就万事大吉。实际上,没触发独立合成层时,scale() 只是让浏览器在主线程里反复光栅化同一块共享绘制区域。
- 确认是否启用硬件加速:检查元素 computed styles 里是否有
will-change: transform或transform: translateZ(0) - 排查
position: relative:它会创建层叠上下文,干扰浏览器对 Grid 子项的图层拆分逻辑 - 避免在父容器上用
overflow: hidden+transform组合:这可能抑制子项独立图层提升
Grid 布局中单个子元素缩放却重绘所有兄弟节点
根本原因不是 Grid 本身有问题,而是浏览器默认把同一 Grid 容器下的所有 grid-area 视为潜在共享绘制边界。当某个 <img class="transition"> 执行 scale(1.5),而它又没获得自己的 GPU 图层,整个 Grid 子树就会被当作一个 paint layer 处理。
实测 Chrome v120+ 下,以下写法会连锁重绘:
<div class="grid grid-cols-3"> @@##@@ @@##@@ </div>
- 删掉
relative可缓解,但失去 z-index 控制能力,不推荐作为长期解 - 加
transform-gpu(Tailwind)或手动加transform: translateZ(0),强制创建独立纹理图层 - 不要滥用
will-change: transform:它不自动创建图层,且过度使用会增加内存压力和纹理上传开销
JS 动画中读取 offsetWidth 后立即改样式导致卡顿
这不是“动画慢”,是同步重排被你亲手触发了。比如写了个循环逐帧调整元素位置,每轮都先读 el.offsetWidth 再设 el.style.left,浏览器只能每次 flush 排队样式、重新 layout、再 paint——帧率直接掉到 10fps 以下。
- 读布局属性前,确保前面没有未提交的样式变更(如刚调过
el.style.width) - 批量读取:把所有要读的
offsetTop、getComputedStyle()放在一起,别穿插在写操作中间 - 能用
requestAnimationFrame批量处理就别用setTimeout:前者天然对齐渲染帧,后者容易错失优化时机 - 替代方案:用
IntersectionObserver替代滚动中反复读scrollTop,用ResizeObserver替代监听window.resize+ 读宽高
innerHTML 批量更新比 appendChild 快,但别乱用
快不是因为字符串解析快,而是浏览器对 HTML 字符串有一条专用的 fast path:它能跳过中间 DOM 构建阶段,直接生成渲染树片段,延迟 layout 直到整个字符串插入完成。
但这个优势只在「纯新增」时成立;如果混着删、改、查,反而更慢。
- 新增 10 个节点:用
document.createDocumentFragment()收集,最后appendChild()一次 - 替换整个区域:先
el.innerHTML = '',再一次性赋值完整 HTML 字符串 - 千万别在循环里写
el.innerHTML += '<div>...</div>':每次 += 都触发一次 parse + layout + paint - 注意 XSS 风险:动态拼接 HTML 时,务必对用户输入做
textContent或DOMPurify过滤
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











