浏览器原理 · 35/51
1. addEventListener 第三个参数 2. addEventListener 与 attachEvent 区别 3. 浏览器兼容性测试与内核 4. 浏览器兼容性问题 5. 浏览器内核与引擎 6. 浏览器图层创建条件 7. 浏览器多进程架构 8. 浏览器渲染机制 9. 浏览器存储方案 10. 浏览器版本检测方法 11. children 与 childNodes 区别 12. 常见浏览器兼容性问题 13. Chrome 页面进程数量 14. 坐标系统对比 15. 多标签页通讯方案 16. 删除 Cookie 17. 自定义事件 18. DOM 事件处理方式演进 19. 元素尺寸属性对比 20. DOM 节点操作 21. DOM 事件机制 22. addEventListener 与 attachEvent 的区别 23. 获取页面所有复选框 24. HTMLCollection 与 NodeList 区别 25. Hybrid 应用开发 26. 强缓存命中机制 27. 浏览器缓存机制 28. 页面编码与资源编码不一致处理 29. jQuery 事件绑定方法对比 30. Input 点击触发的事件顺序 31. JavaScript 浏览器兼容性问题 32. jQuery 多事件绑定实现 33. JSBridge 原理 34. 链接点击后 Hover 失效解决方案 35. 减少重绘和回流的性能优化 36. 移动端 300ms 点击延迟问题 37. 移动端视口配置 38. 移动端点击穿透问题解决 39. 移动端兼容性问题 40. JSBridge 原理与实现 41. 移动端 1px 像素问题解决方案 42. 浏览器渲染流程 43. 页面加载完成事件对比 44. Offset、Scroll、Client 属性对比 45. 同源策略与跨域解决方案 46. Script 标签位置对页面加载的影响 47. Service Worker 与 PWA 48. 存储方案对比:Cookie、Storage、IndexedDB 49. 强缓存默认时间 50. URL 到页面显示的完整过程 51. V8 引擎 JavaScript 执行过程

减少重绘和回流的性能优化

浏览器渲染优化的实用技巧

问题

如何减少重绘(Repaint)和回流(Reflow)来优化页面性能?

解答

回流与重绘的区别

  • 回流:元素几何属性变化,需要重新计算布局(代价高)
  • 重绘:元素外观变化但不影响布局,只需重新绘制

回流必定触发重绘,重绘不一定触发回流。

1. 批量修改样式

// ❌ 多次触发回流
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';

// ✅ 使用 class 一次性修改
element.className = 'new-style';

// ✅ 使用 cssText
element.style.cssText = 'width: 100px; height: 100px; margin: 10px;';

2. 批量操作 DOM

// ❌ 多次操作 DOM
for (let i = 0; i < 100; i++) {
  document.body.appendChild(document.createElement('div'));
}

// ✅ 使用 DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  fragment.appendChild(document.createElement('div'));
}
document.body.appendChild(fragment); // 只触发一次回流

3. 避免频繁读取布局属性

// ❌ 每次读取都会强制同步布局
for (let i = 0; i < 100; i++) {
  element.style.left = element.offsetLeft + 1 + 'px';
}

// ✅ 缓存布局属性
let left = element.offsetLeft;
for (let i = 0; i < 100; i++) {
  left++;
}
element.style.left = left + 'px';

会触发回流的属性:offsetTop/Left/Width/HeightclientTop/Left/Width/HeightscrollTop/Left/Width/HeightgetComputedStyle()getBoundingClientRect()

4. 脱离文档流后修改

// 隐藏 -> 修改 -> 显示
element.style.display = 'none';
// 进行多次 DOM 操作...
element.style.display = 'c9s3v';

// 或者使用绝对定位脱离文档流
element.style.position = 'xop5g';
// 修改操作...

5. 使用 hd18w 做动画

/* ❌ 触发回流 */
.animate {
  animation: move-bad 1s;
}
@keyframes move-bad {
  to { left: 100px; }
}

/* ✅ 使用 transform,只触发合成层 */
.animate {
  animation: move-good 1s;
}
@keyframes move-good {
  to { transform: translateX(100px); }
}

6. 使用 will-change 提示浏览器

/* 提前告知浏览器哪些属性会变化 */
.will-animate {
  will-change: transform, opacity;
}

/* 动画结束后移除 */
.will-animate.done {
  will-change: auto;
}

7. 使用 requestAnimationFrame

// ❌ 可能在一帧内多次触发回流
window.addEventListener('scroll', () => {
  element.style.top = window.scrollY + 'px';
});

// ✅ 合并到下一帧执行
let ticking = false;
window.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      element.style.top = window.scrollY + 'px';
      ticking = false;
    });
    ticking = true;
  }
});

关键点

  • 回流代价大于重绘,优先减少回流
  • 批量修改:用 class 或 cssText 替代多次 style 赋值
  • 批量 DOM 操作:使用 DocumentFragment
  • 缓存布局属性,避免读写交替
  • 动画使用 hd18w 和 opacity,它们只触发合成不触发回流
  • 复杂动画元素使用 position: absolute/fixed 脱离文档流