浏览器

渲染引擎

世界五大浏览器,在自身发展时都使用了一种或多种渲染引擎作为自身的渲染引擎。

  • Google Chrome:​​Webkit​​​(前期)、​​Blink​​(后期)
  • Apple Safari:​​Webkit​
  • Mozilla Firefox:​​Gecko​
  • ASA Opera:​​Presto​​​(前期)、​​Blink​​(后期)
  • Microsoft IExplorer:​​Trident​
  • Microsoft Edge:​​Trident​​​(前期)、​​Blink​​(后期)

关键渲染路径

关键渲染路径指浏览器从最初接收请求得到​​HTML​​​、​​CSS​​​、​​JS​​等资源,然后解析、构建、渲染、布局、绘制和合成,到最后呈现在用户眼前的整个过程。

将关键渲染路径划分理解,那网页的渲染过程可分为以下部分。


解析文件

  • ​DOM树​​​:将​​html文件​​转换为​​DOM树​
  • ​CSSOM树​​​:将​​css文件​​转换为​​CSSOM树​
  • ​渲染树​​​:将​​DOM树​​与​​CSSOM树​​合并生成​​渲染树​

绘制图层

  • ​回流​​:根据渲染树生成布局渲染树
  • ​重绘​​:根据布局渲染树生成绘制渲染树
  • 合成图层:根据绘制渲染树合成图层显示在屏幕中

解析文件

​HTML文档​​​描述网页的结构,浏览器通过​​HTML解析器​​​将​​HTML​​​解析为​​DOM树结构​​​。​​HTML文档​​​中所有内容皆为节点,各节点间拥有层级关系,彼此相连,组成​​DOM树​​。

构建​​DOM树​​​的过程:读取​​HTML文档​​的字节(Bytes),将字节转换成字符(Chars),根据字符确定标签(Tokens),将标签转换成节点(Nodes),以节点为基准构建DOM树

​CSS文档​​​描述网页的表现,浏览器通过​​CSS解析器​​​将​​CSS​​​解析为​​CSSOM树结构​​​,与​​DOM树结构​​​较像。​​CSS文档​​​中所有内容皆为节点,与​​HTML文档​​​中的节点一一相应,各节点间拥有层级关系,彼此相连,组成​​CSSOM树​​。

构建​​CSSOM树​​​的过程:读取​​CSS文档​​的字节(Bytes),将字节转换成字符(Chars),根据字符确定标签(Tokens),将标签转换成节点(Nodes),以节点为基准构建CSSOM树。与​​DOM树​​的构建过程完全一样。

在构建​​DOM树​​​时,当​​HTML解析器​​​遇到​​<script>​​​时会立即阻塞​​DOM树​​​的构建,将控制权移交给浏览器的​​JS引擎​​​,等到​​JS引擎​​​运行完毕,浏览器才会从中断的地方恢复​​DOM树​​的构建。

      HTML






<script>的脚本加载完毕,JS引擎通过DOM API与CSSOM API操作DOM树与CSSOM树。为何会产生渲染阻塞?其根本原因在于JS操作DOM后,浏览器无法预测未来DOM的具体内容,为了防止无效操作与节省资源,只能阻塞DOM树的构建。

浏览器的渲染引擎将​​DOM树​​​与​​CSSOM树​​​合并生成渲染树,只渲染需显示的节点及其样式。​​DOM树​​​、​​CSSOM树​​​和​​渲染树​​​三者的构建并无​​先后条件​​​与​​先后顺序​​,非完全独立而是存在交叉并行构建的情况,因此会形成一边加载,一边解析,一边渲染的工作现象。

绘制图层

进入绘制阶段,遍历渲染树,调用渲染器的​​paint()​​​在屏幕中绘制内容。根据渲染树布局计算样式,即每个节点在网页中的布局、尺寸等几何属性。​​HTML​​​默认是流式布局,​​CSS​​​与​​JS​​​会打破这种布局,改变​​DOM​​的几何属性与外观属性。在绘制时根据渲染树布局,再根据布局绘制,这就是回流重绘

在此涉及两个核心概念:回流重绘。我用两句精简的话概括它们。

  • 回流:改变几何属性的渲染
  • 重绘:改变外观属性而不影响几何属性的渲染

当生成渲染树后,至少会渲染一次,在后续交互时还会不断地重新渲染。这时​​只会回流重绘​​​或​​只有重绘​​,因此引出一个定向法则:回流必定引发重绘,重绘不一定引发回流

合成图层

将回流重绘生成的图层逐张合并并显示在屏幕中。上述几个步骤并非一次性顺序完成,若改动​​DOM/CSSOM​​​,上述过程会被重新执行,实际上​​CSS​​​与​​JS​​​往往会多次改动​​DOM/CSSOM​​。简而言之,用户的交互操作引发了网页的重渲染。

加入浏览器私有属性

通常编写​​CSS​​​都会在一些​​CSS3属性​​​前加入​​-webkit-​​​、​​-moz-​​​、​​-ms-​​​或​​-o-​​​,这些奇形怪状写到手软的东西就是​​浏览器私有属性​​。

对于编写私有属性的顺序需特别注意:兼容性写法放到前面,标准性写法放到最后。在浏览器解析​​CSS​​时,若标准属性无法使用则使用当前浏览器相应私有属性。

/* Chrome、Safari、New Opera、New Edge */
-webkit-transform: translate(10px, 10px);
/* Firefox */
-moz-transform: translate(10px, 10px);
/* IExplorer、Old Edge */
-ms-transform: translate(10px, 10px);
/* Old Opera */
-o-transform: translate(10px, 10px);
/* 标准 */
transform: translate(10px, 10px);

当然不是所有​​CSS3属性​​​都需补齐​​-webkit-​​​、​​-moz-​​​、​​-ms-​​​或​​-o-​​​,上述代码只是一个示例,真正的​​transform​​​私有属性只有​​-webkit-​​​与​​-ms-​​​。这些私有属性需查看​​Caniuse​​以确保编写正确,若想偷懒也可全部写上。

每个​​CSS3属性​​​都编写这么一堆兼容性代码,无疑是对生命最大的浪费。在使用​​webpack​​​打包代码时,可接入​​postcss-loader​​​与​​postcss-preset-env​​​,​​postcss-preset-env​​​内置了​​autoprefixer​​​,它会根据​​Caniuse​​​提供的数据对代码中的​​CSS3属性​​批量加入私有属性。

​Can I use... Support tables for HTML5, CSS3, etc​