- 浏览器
- 浏览器渲染原理
- 渲染引擎
- 解析 HTML:将 HTML 转换为 DOM 树(Document Object Model)。
- 解析 CSS:将 CSS 转换为样式规则,并应用到 DOM 树。
- 构建渲染树:根据 DOM 树和样式规则生成渲染树。
- 布局(Layout):计算每个元素的大小和位置。
- 绘制(Painting):将渲染树中的每个元素绘制到屏幕上。
- 渲染过程
- 首先解析收到的文档,根据文档定义构建一棵 DOM 树,DOM 树是由 DOM 元素及属性节点组成的。
- 然后对 CSS 进行解析,生成 CSSOM 规则树。
- 根据 DOM 树和 CSSOM 规则树构建渲染树。渲染树的节点被称为渲染对象,渲染对象和 DOM 元素相对应,但这种对应关系不是一对一的,不可见的 DOM 元素不会被插入渲染树。
- 当渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。
- 布局阶段结束后是绘制阶段,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。
- 注意:这个过程是逐步完成的,为了更好的用户体验,渲染引擎会尽可能早的将内容呈现到屏幕上,并不会等到所有的 html 都解析完成之后再去构建和布局 render 树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
- 渲染优化
- 网络加载优化
- 资源合并和压缩,减少 HTTP 请求次数和文件大小
- 利用浏览器缓存,包括 HTTP 缓存、资源预加载等
- 使用 CDN 加速资源加载
- CDN 可以通过地理位置优化、负载均衡、缓存优化、连接复用以及抗攻击能力等多种方式,加速静态资源的加载,提升网站的性能和用户体验
- HTML 解析优化
- 减少 DOM 元素数量,避免嵌套过审的结构
- 使用语义化标签,帮助浏览器更快理解文档结构
- 尽量将 script 标签放在 body 最后,或者用 async 或者 defer 的方式引入 script
- async:下载 JS 是异步,下载完成会立即执行,会阻塞渲染
- defer:下载是异步的,会等 DOM 树构建完毕,并且在 DOMContentLoaded 事件触发之前执行
- CSS 渲染优化
- 避免使用过多的样式规则和选择器,减少 CSS 文件大小
- 使用简单的 CSS 动画而不是 JS 动画,以利用浏览器的硬件加速功能
- 布局(回流/重排)、绘制(重绘)优化
- 尽量减少不必要的元素重绘,例如使用 transform 属性而不是修改 top 和 left 属性来移动元素
- 使用 CSS3 动画或变换来减少重绘和回流
- GPU 加速:大部分浏览器使用 GPU 加速 CSS3 动画。GPU 通常能够处理图形任务更快,因此将动画委托给 GPU 处理可以提高动画的性能,减少 CPU 的负载。相比之下,JavaScript 动画通常由 CPU 处理,性能可能不如 GPU 加速的 CSS3 动画。
- 硬件加速的转换和动画:CSS3 动画可以应用于 CSS 属性,如 transform 和 opacity,这些属性可以通过硬件加速进行处理,而不需要触发重绘或回流。例如,使用 transform 属性进行动画时,浏览器可以在 GPU 上执行动画转换,而不会影响文档流,从而减少了重绘和回流。
- 优化的动画循环:浏览器能够优化 CSS3 动画的执行方式。例如,浏览器可以根据屏幕刷新率进行动画绘制,以确保动画流畅且不会引起撕裂或闪烁。
- 合成优化
- 利用 GPU 加速,将合成和绘制操作委托给 GPU
- 怎么实现:利用 CSS3 属性、3D 变换、will-change 属性以及避免触发重绘和回流等方法,可以有效地利用 GPU 加速,将合成和绘制操作委托给 GPU,从而提高页面的性能和流畅度
- 使用 CSS 属性 will-change 来提示浏览器元素将要被修改,以便浏览器在后台优化渲染。
- 回流和重绘
- 回流
- 渲染树中部分或全部元素的尺寸、结构或者属性发生变化时,浏览器全部或部分重新渲染文档的过程,就是回流
- 导致回流的操作
- 页面首次渲染
- 浏览器窗口大小变化
- 页面内容、字体大小、激活伪类
- 查询元素的宽高
- 添加或删除可见 DOM 元素
- 重绘
- 样式变化,但是不影响元素在文档流中的位置时,浏览器就会对元素进行重绘
- color、background、border-radius、visibility、opaticy
- 如何避免
- 操作 DOM 时,尽量在低层级的 DOM 节点进行操作
- 不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式。
- 不要使用 table 布局, 一个小的改动可能会使整个 table 进行重新布局
- 使用 absolute 或者 fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
- 避免频繁操作 DOM,可以创建一个文档片段 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中
- DOM 离线化:将元素先设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。
- 将 DOM 的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于浏览器的渲染队列机制。
- 渲染 Flush 队列
- 浏览器针对页面的回流与重绘,进行了自身的优化,就是渲染队列。
- 浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行批处理。这样就会让多次的回流、重绘变成一次回流重绘。
- 上面,将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,原本应该是触发多次回流,变成了只触发一次回流。