介绍浏览器的渲染过程, 页面加载过程, 渲染过程中的性能优化等.
渲染机制
浏览器的渲染机制一般分为以下几个步骤
- 处理 HTML 并构建 DOM 树
- 处理 CSS 构建 CSSOM 树
- 将 DOM 与 CSSOM 合并成一个渲染树
- 根据渲染树来布局, 计算每个节点的位置
- 调用 GPU 绘制, 合成图层, 显示在屏幕上

在构建 CSSOM 树时, 会阻塞渲染, 直到 CSSOM 树构建完成. 并且构建 CSSOM 树是一个十分消耗性能的过程, 所以应该尽量保证层级扁平, 减少过度层叠, 越是具体的 CSS 选择器, 执行速度越慢.
当 HTML 解析到 script 标签时, 会暂停构建 DOM, 完成后才会从暂停的地方重新开始. 也就是说, 如果你想首屏渲染的越快, 就越不应该在首屏就记载 JS 文件. 并且 CSS 也会影响 JS 的执行, 只有当解析完样式表才会执行 JS, 所以也可以认为这种情况下, CSS 也会暂停构建 DOM.
Load 事件和 DOMContentLoaded 事件的区别
Load 事件触发代表页面中的 DOM, CSS, JS, 图片已经全部加载完毕.
DOMContentLoaded 事件触发代表初始的 HTML 被完全加载和解析, 不需要等待 CSS, JS, 图片加载.
图层
一般来说, 可以把普通文档流看成一个图层. 特定的属性可以生成一个新的图层. 不同的图层渲染互不影响, 所以对于某些频繁需要渲染的建议单独生成一个新图层, 提高性能. 但也不能生成过多的图层, 会引起反作用.
如何生成新图层:
- 3D 变换:
translate3d,translateZ will-changevideo,iframe标签- 通过动画实现的
opacity动画转换 position:fixed
重绘(Repaint) 和 回流(Reflow)
回流必定会发生重绘, 重绘不一定会引起回流.
重绘和回流是渲染步骤中的一个小节, 但是这两个步骤对于性能影响很大.
- 重绘是当节点需要更改外观而不影响布局的变化, 比如改变
color就会导致重绘 - 回流是布局或者几何属性需要改变, 就成为回流
回流所需的成本比重绘高的多, 改变深层次的节点很可能导致父节点的一系列回流
所以以下几个动作可能会导致性能问题:
- 改变 window 大小
- 改变字体
- 添加或删除样式
- 文字改变
- 定位或者浮动
- 盒模型
另外, 重绘和回流跟 Event loop 有关
- 当 Event loop 执行完 Microtasks 后, 会判断 document 是否需要更新. 因为浏览器是 60Hz 的刷新率, 每 16ms 才会更新一次
- 然后判断是否有
resize或者scroll, 有的话会触发事件, 所以resize和scroll事件也是至少 16ms 才会触发一次, 并且自带节流功能 - 判断是否触发了 media query
- 更新动画并且发送事件
- 判断是否有全屏操作事件
- 执行
requestAnimationFrame回调 - 执行
IntersectionObserver回调, 该方法用于判断元素是否可见, 可以用于懒加载上, 但是兼容性不好 - 更新界面
- 以上就是一帧中可能会做的事情. 如果一帧中有空闲时间, 就会去执行
requestIdleCallback回调
如何减少重绘和回流
- 使用
translate代替top - 使用
visibility代替display:none, 因为前者只会引起重绘, 后者会引起回流(改变了布局) - 把 DOM 离线后修改, 比如: 先把 DOM 设置
display:none, 然后修改 100 次, 然后再把它显示出来 - 不要把 DOM 节点的属性值放在一个循环里当成循环里的变量
- 将频繁运行的动画变为图层, 图层能够阻止该节点回流影响别的元素. 比如对于 video 标签, 浏览器会自动将该节点变为图层
1 | <div class="test"></div> |
从输入 URL 到网页加载完成的过程
- 首先做 DNS 查询, 如果这一步做了智能 DNS 解析的话, 会提供访问速度最快的 IP 地址回来
- TCP 握手, 应用层会下发数据给传输层, 这里 TCP 协议会指明两端的端口号, 然后下发给网络层. 网络层中的 IP 协议会确定 IP 地址, 并且指示了数据传输中如何跳转路由器. 然后包会再被封装到数据链路层的数据帧结构中, 最后就是物理层面的传输
- TCP 握手结束后会进行 TLS 握手, 然后就开始正式的传输数据
- 数据再进入服务端之前, 可能还会先经过负责负载均衡的服务器, 它的作用就是将请求合理的分发到多台服务器上, 这时假设服务端会相应一个 HTML 文件
- 首先浏览器会判断状态码是什么, 如果是 200 那就继续解析, 如果 400 或 500 的话就会报错, 如果 300 的话就重定向, 这里会有个重定向计数器, 避免过多次的重定向, 超过次数也会报错
- 浏览器开始解析文件, 如果是 gzip 格式就先解压, 然后通过文件的编码格式知道该如何去解码文件
- 文件解码成功后会正式开始渲染流程, 先会根据 HTML 构建 DOM 树, 有 CSS 的话会去构建 CSSOM 树. 如果遇到
script标签的话, 会判断是否存在async或者defer, 前者会并行进行下载并执行 JS, 后者会先下载文件, 然后等待 HTML 解析完成后顺序执行, 如果以上都没有, 就会阻塞住渲染流程直到 JS 执行完毕. 遇到文件下载的会去下载文件, 这里如果使用 HTTP 2.0 协议的话会极大的提高多图的下载效率 - 初始的 HTML 被完全加载和解析后会触发
DOMContentLoaded事件 - CSSOM 树和 DOM 树构建完成后会开始生成 Render 树, 这一步就是确定页面元素的布局,样式等诸多方面的东西
- 在生成 Render 树的过程中, 浏览器就开始调用 GPU 绘制, 合成图层, 将内容显示在屏幕上
一道渲染相关的面试题
如何渲染几万条数据并不卡住界面?
1 |
|