谈谈浏览器渲染

420 阅读3分钟
面试的时候总被问到浏览器渲染原理,其实这块可大可小,所以写篇文章回顾下

当浏览器拿到HTML文档时首先会进行HTML文档解析,构建DOM树。DOM树的构建是从接收文档开始的,然后一边将字节转化为字符,字符转化为标记。这个过程是一个渐进式的,一边接收和处理网络中的其他内容,呈现引擎也会解析部分内容尽快展示出来。

遇到link或者style标签,开始解析css,构建样式树。HTML解析构建和CSS的解析是相互独立的并不会造成冲突,因此我们通常将css样式放在head中,让浏览器尽早解析css。

解析js,js会通过api来操作DOM 树和css rule 树。js的解析会暂停DOM解析,所以推荐把js脚本放在最后。其中js文件的异步,指的是下载异步,暂停指的是执行。

解析完成之后,浏览器引擎会通过DOM 树和css rule 树来构建rendering树。css rule树会挂载到DOM树上。

  • DOM树和rendering树并不完全相同。在视觉中不显示的都不会渲染,比如head,比如display:none的元素。

rendering树完成后,会进行几何布局。布局一般叫做layout,不过不同引擎的算法有一些区别叫法也不相同。

  • layout是一种流失布局的算法。基本是通过从上到下,从左到右的方式。
  • 不过实现的时候并不完全是这么干的。大概是这样:
    • 父元素放入到自己的位置
    • 子元素绘入到父元素
    • 子元素确定宽高位置
    • 父元素根据子元素的情况,确定自己的宽高。
  • 通过js获取元素宽高,会引起layout。但是在实际情况下引擎其实不会这么干。

layout完成后,浏览器开始绘制这些元素的样式,颜色,背景,大小及边框等,这一步也叫做repaint。

理论上来说浏览器会直接展示位图,但是其实浏览器为了方便处理定位等,浏览器需要生成另外一棵树 —— 层树。原理如下:

  • 根据DOM结构切分为不同的层。
  • 将每一个层栅格化后,独立绘制。
  • 上传上面的进入GPU。
  • GPU会将各层合成,显示在屏幕上。

合成

常规的页面中,最频繁的DOM操作其实是动画这块。比如通过js来实现一个轮播图,大概的原理就是,不停的改变这组图片的位置来得到视觉上的变化。但是这样处理结合我们上面说到的大概是这样的。

  1. 每一次变换位置,我们改变他的一个位置属性。
  2. 这样触发layout规则,会重新计算几何位置。
  3. 触发repaint,重新渲染。
  4. 推到GPU中,重新合成。

这样的话,会给浏览器的主进程、排版进程、GPU,塞入大量的任务,造成页面的卡顿。

根据上面的步骤我们可以想下,既然我们看到的东西都是通过GPU来完成的,为啥不能只触发这块呢?

其实是可以的。满足条件的有transform和opacity,所以上面的动画可以通过css的来是实现。大概一个实现如下:

.a{
  animation: run 1s linear;
}
@keyframes run {
 from { transform: translateX(0); }
 to { transform: translateX(200px); }
}

总结下优化的策略

  • 减少浏览器的重排和重绘的发生。
  • 不要使用table布局。
  • css动画中尽量只使用transform和opacity,这不会发生重排和重绘。
  • 尽可能地只使用css做动画。
  • 改变复合层的尺寸。