性能优化 - 渲染优化

概念理解

回流(Reflow):当元素的布局属性(如宽度、高度、位置等)发生变化时,浏览器需要重新计算元素的几何属性,并重新布局页面,这个过程称为回流(页面首次渲染时叫做 Layout 布局)。

回流影响范围

  • 全局范围:从根节点开始,对整个渲染树进行重新布局。
  • 局部范围:对渲染树的某部分或者一个渲染对象进行重新布局。

重绘(Repaint):当元素的外观属性(如颜色、背景、边框等)发生变化,但不影响布局时,浏览器只需要重新绘制元素,这个过程称为重绘。

记住:当触发回流时,一定会触发重绘,但是重绘不一定会触发回流。

触发回流的情况

  • 页面的首次渲染。
  • 浏览器的窗口大小发生变化(滚动条)。
  • 元素的内容发生变化。
  • 元素的尺寸或者位置发生变化:宽高、边框、切换定位、浮动、display。
  • 元素的字体大小发生变化:font-sizefont-familywhite-space
  • 查询某些属性或者调用某些 API:offset/client/scrollXXXgetComputedStyle,滚动 scrollTo()
  • 添加或者删除可见的 DOM 元素:appendChildinsertBeforeremoveChild

只触发重绘不会触发回流的属性

  • border-radius:边框圆角。
  • visibility:元素可见性。
  • box-shadow:盒子阴影。
  • color:文字颜色。
  • background-color:背景颜色。
  • outline:轮廓。
  • opacity:透明度(在合成层中)。

减少回流与重绘的措施

1. DOM 操作优化

  • 尽量在低层级的 DOM 节点进行操作:减少影响范围,避免触发大范围的回流。
  • 不要使用 table 布局:可能会使整个 table 进行重新布局。
  • 修改类名:而不是直接修改样式,通过类名切换批量修改样式。
  • 使用 absolute 或者 fixed:使元素脱离文档流,这样它们发生变化就不会影响其他元素。
  • 将元素先设置 display: none:操作结束后再把它显示出来。此时元素 DOM 操作不会引发回流和重绘。
  • 避免 CSS 表达式 :CSS 表达式(如 calc())会被频繁计算,导致性能问题,应避免使用。

2. 文档片段 DocumentFragment

DocumentFragment 文档片段接口,是没有父对象的最小文档对象,和正常的文档节点差不多,但在上面添加和删除节点,不会触发真实 DOM 树的重新渲染。

当 DocumentFragment 插入到文档中时,会一次性插入它所有的子孙节点,不会和直接操作 DOM 树一样引起频繁的重排和重绘。

javascript 复制代码
const fragment = document.createDocumentFragment();
// 应用所有 DOM 操作后再插入文档
for (let i = 0; i < 1000; i++) {
  const li = document.createElement("li");
  li.textContent = `Item ${i}`;
  fragment.appendChild(li);
}
document.getElementById("list").appendChild(fragment);

3. 渲染队列机制

将 DOM 的多个读操作或写操作放在一起,避免读写操作穿插着写。

浏览器存在渲染队列机制,执行写操作会先放一起累计,等到一定程度/触发读时再执行重排重绘。

javascript 复制代码
// ❌ 读写操作穿插
const width = element.offsetWidth; // 触发回流
element.style.width = width + 10 + "px"; // 写操作
const height = element.offsetHeight; // 触发回流
element.style.height = height + 10 + "px"; // 写操作

// ✅ 批量写操作
element.style.width = element.offsetWidth + 10 + "px";
element.style.height = element.offsetHeight + 10 + "px";

动画优化

1. 使用定位属性脱离文档流

使用 absolutefixed 脱离文档流,使动画元素不影响其他元素的布局。

2. 使用 transform 和 opacity

使用 transformopacity 进行动画,这些属性可以在合成层中由 GPU 处理。

浏览器会将使用这些属性的元素提升为合成层,在合成器线程中直接处理,具体触发渲染位置查看 浏览器帧渲染流程理解,无需重新计算布局和绘制,无需参与主线程的布局和重绘流程。

优势

  • transform:在合成层中处理,不会触发回流和重绘。
  • opacity:在合成层中处理,不会触发回流和重绘。

注意 :使用 transform3D 可以提升 GPU 合成层完成动画操作,但不宜过多合成层,会占用较大内存。

示例

css 复制代码
/* ❌ 可能使用会触发回流的属性 */
.element {
  animation: move 1s;
}
@keyframes move {
  from {
    left: 0;
  }
  to {
    left: 100px;
  }
}

/* ✅ 使用 transform 3D  */
.element {
  animation: move 1s;
}
@keyframes move {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100px);
  }
}

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

使用 will-change 属性提前告知浏览器元素将要发生变化,浏览器可以提前优化。

注意 :不过度使用 will-change,因为会创建新的合成层,占用内存。

css 复制代码
.element {
  will-change: transform;
}

其他

  • 仅渲染屏幕内容 :减少屏幕绘制内容,也可以达到优化效果。
    • 虚拟滚动:使用虚拟滚动插件,虚拟滚动本质是通过创建一个超大或符合展示所有数据的容器,用于模拟滚动效果,然后计算数据项的索引值,每次只渲染界面中出现的内容。
    • Intersection Observer:监听内容是否在屏幕内,是则渲染,不是则保留布局容器,其余隐藏,减少布局渲染压力。在渲染大量彩色文本内容,需要嵌套多层元素的时候非常有用。
    • content-visibility:auto :CSS 属性,可以实现离屏降低渲染压力的效果,搭配 contain-intrinsic-size 可以避免滚动条塌陷,具体参考 content-visibility

总结

  • 核心概念:回流会重新计算布局,重绘只重新绘制外观。回流一定会触发重绘,但重绘不一定触发回流。

  • 优化策略

    • DOM 操作优化:在低层级节点操作、使用类名切换、脱离文档流、批量操作。
    • 使用 DocumentFragment:批量 DOM 操作时使用,减少回流和重绘次数。
    • 渲染队列机制:批量读写操作,避免读写穿插。
    • 动画优化 :使用 transformopacity 进行动画,利用 GPU 合成层。
  • 注意事项

    • 避免使用 table 布局和 CSS 表达式。
    • 合理使用 will-change,避免创建过多合成层。
    • 优先使用只触发重绘的属性,避免触发回流。
相关推荐
YCY^v^17 小时前
JeecgBoot 项目运行指南
java·学习
夏幻灵17 小时前
HTML5里最常用的十大标签
前端·html·html5
云小逸17 小时前
【nmap源码解析】Nmap OS识别核心模块深度解析:osscan2.cc源码剖析(1)
开发语言·网络·学习·nmap
冰暮流星17 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_17 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝17 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions17 小时前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发17 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
程序员猫哥_17 小时前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞0517 小时前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl