有一次面试,面试官问完_从输入URL到页面展示,这中间发生了什么?本人对答如流,面试官微微一笑,问浏览器的渲染流程是怎么样的呢**?_**那一刻静的太可怕。赶紧回家整理一番。
渲染流程就是指:浏览器拿到编译好的js,html,css是怎么渲染成页面的。
1. 渲染流水线
渲染机制过于复杂,渲染模块会把执行过程划分成很多的子阶段,经过这些子阶段,html才最终输出为像素。这样的一个处理流程就叫做渲染流水线。
流水线可分为如下几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。
2. 各个子阶段
构建DOM树
由于浏览器无法直接识别和使用HTML,所以需要将HTML转换为浏览器可以理解的DOM树,如下图。
图片获取于网络非作者原创
样式计算
样式计算目的是,计算出DOM节点中元素的具体样式,分为三步执行。
- 浏览器同样不能直接使用css,需要转换成styleSheets,在Chrome控制台输入document.styleSheets,就可以看到转换后的结构。
- 我们经常用到1em,blue, bold这样的值,其实渲染引擎是 不理解这些值的,所以就需要进行标准化计算,如下图。
- 接下来,就是计算出DOM树每个节点的具体样式。进行样式计算时需要遵循两个规则:继承和层叠,继承就是基层父节点的样式,层叠是指合并多个源的属性值的算法。
图片获取于网络非作者原创
布局阶段
当DOM树和DOM树中元素的样式就位之后,还需要计算出DOM元素的几何位置信息,这个过程就叫做布局。该阶段分两步:创建布局树和布局计算。
- DOM树中有许多不可见的元素(head标签,使用display:none的等),所以还需要生成一个可见的布局树,浏览器主要做的工作是,遍历DOM树,把可见的节点加入到布局树中。
- 接下来就是布局计算,这个过程非常复杂,大佬们可以在留言区指教。
创建布局树(图片获取于网络非作者原创)
分层阶段
我们开发的页面中,总会有一些3D效果,滚动等效果。对于这种情况,渲染引擎还需要为特定的元素节点生成对应的图层,并生成一颗图层树。在Chrome的开发者工具,选择“Layers”标签,就可以看到当前页面分层的情况。
-
浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面。
-
并不是每一个节点都包含一个图层,如果一个节点没有对于的图层,那么这个节点就从属父节点的图层。
-
拥有层叠上下文的属性会被单独提升到一层。
-
还有当div中的文字过多,渲染引擎就需要进行裁剪换行,这时候文字就在单独的一个图层,如果有滚动条,滚动条也会单独提升到一个图层。
层叠上下文的属性
图层绘制阶段
举个例子:如果给一张纸,先把纸的背景涂成蓝色,然后在中间位置画一个红色的圆,最后再在圆上画个绿色三角形。会怎么操作呢?
通常,就是分解为三步:
1. 绘制蓝色背景;
2. 在中间绘制一个红色的圆;
3. 再在圆上绘制绿色三角形。
渲染引擎实现图层绘制和这个例子差不多,把图层绘制拆分成很多小的绘制指令。如下图,可以看出绘制的指令比较简单。
图片获取于网络非作者原创
在Chrome开发者工具,打开”Layers”,选择“document”层,就可用看到绘制指令的列表。
栅格化(raster)操作
上面我们讲到的绘制指令列表,它只是一个描述记录绘制的列表,实际的绘制工作是由渲染引擎中的合成线程来完成的。
图片获取于网络非作者原创
主线程把绘制列表提交给合成线程,进而进行绘制工作。
我们浏览器窗口尺寸是有限的(视口),对于图层较大的页面,很多内容是“暂时性隐藏“”的(滚动区域),这种情况下一下子渲染出所有的图层,势必造成渲染开销太大,而且对于视口之外内容也没有必要一并渲染。
所以,**合成线程会把图层划分为图块,会按照视口附近的图块来优先生成位图,那么生成位图的操作就是由栅格化来执行的,栅格化就是将图块转化为位图。**渲染进程会单独开辟出一个栅格化的线程池,所有的图块栅格化都是在线程池完成的。
合成操作
合成已经到了渲染的最后一个步骤,一旦所有的图块被栅格化,合成线程就会向浏览器进程发送一个绘制图块的命令。浏览器进程中的viz组件接受到命令后,将页面内容绘制到内存中,最后将内存内容显示到屏幕上。
完整的渲染流程