概述
前言
老规矩,问题:html,css,js如何变成用户能与之交互的页面的?
上篇博客已经介绍了导航的流程, 那么现在就是具体的渲染流程了
如果对这个有很深刻的了解,那么你就看透页面是如何工作的,有了这些知识
就能解决一系列相关的问题,比如使用开发者工具,优化页面卡顿问题,使用js优化动画流程,通过样式表来防止强制同步布局等等
html,css,js这些非常基础的我就不说了,对门他们的理解从架构方向上是html控制结构,css控制样式,js控制行为.
渲染模块太复杂了,我们可以把他大致分为几个子流程
html,css,js => 子流程=>子流程=>子流程=>…=>页面
按照时间顺序,大概就是这么个流程把
构建Dom树=>样式计算=>布局阶段=>分层=>绘制=>分块=>栅格化=>合成
emmm…确实挺多的 方便记忆的话你可以使用编译原理中的有限状态机画出来(如果你学过编译原理)
重点看这个点
- 每个阶段的输入是什么
- 每个阶段的处理是什么
- 每个阶段的输出又是什么
其实直接用状态机更简单,不过我懒得画了,码字真的累…
好了,正式走流程
构建DOM树
为什么要构建DOM树呢?因为浏览器无法直接使用和理解html,计算机是很笨的,当然也很聪明,你必须为他指明一套规则,他才能按这个规则去解析,dom树就是一种浏览器能解析的数据结构 ,数据结构这块呢,学计算机的同学一定要学好,别像我,了解了他的重要性之后重新学…都是教训
构建dom树的输入内容就是一个非常简单的html文件,里面不包含css或者js,就是纯粹的html,可以直接打开控制台打印document,打印出来的就是一个dom树结构,dom树内容几乎与html一摸一样,但是dom树是保存在内存树状结构中,可以通过js来动态修改的
我们生成dom树之后,dom的样式我们是无法知道的,因为这阶段是不管样式的
样式计算
样式计算的目的就是为了计算出dom节点每个元素的具体样式,大体可以分为3个部分
1.把css转换为浏览器理解的结构
css的来源主要有哪些呢?
一个是link标签,@import引入,或者style标签内的css,内嵌的style样式
这里也就不展开讲上面几种的区别了,刚兴趣的话具体查link和import的区别就行
css和html一样,浏览器无法直接理解,所以渲染引擎接收到css文本时,执行一个转换操作,将css文本转化成浏览器可以理解的-----styeleSheets 样式表
也可以直接在控制台打印 document.styleSheets查看样式表
这个样式表把以上几种来源的样式全部加入,变成这种数据结构,并且能够具备查询和修改的功能,为后面js控制样式操作提供基础
2.转换样式表中的属性值
现在样式表已经能被浏览器认识了,接下来对其属性值进行标准化操作
什么是属性值标准化呢?
body{font-size:2em}
p{color:blue}
span{display:none}
div{font-weight:bold}
div p{color:green}
上面这个随便写的css样式属性值有多不同的东西,什么em单位,blue,bold,之类的,这些浏览器其实要么不认识的,要么说不便于理解,所以把这些值转换为渲染引擎容易理解的,标准化的计算值,这个过程就是属性值标准化
到时候em单位变成px单位,颜色blue之类的变成rgb单位
3.计算出dom树中每个节点的具体样式
样式标准了之后,就开始计算dom每个节点的样式属性了
怎么计算就涉及到css的继承规则和层叠规则了
层叠规则是css的一个基本特征,规定了如何合并来自多个源的属性值的算法(包括权重问题,优先级问题)
上面的一些规则就包括css权重的一些内容,面试也会问道,这里不多说
样式计算阶段的目的就是计算出dom节点中每个元素的具体样式,计算中遵守继承规则和层叠规则,最终输出每个dom节点的样式,保存在computedStyle结构内
直接用开发者工具选择element中computed也可以查看
布局阶段
现在,有DOM树和Dom树中元素的样式,但是还不足以显示页面,因为我们还不知道几何位置的信息,接下来就需要计算出dom元素中占据位置的元素
的几何位置,我们把这个过程叫做布局
1.创建布局树
dom树中还有很多不可见的元素,比如head标签,还有使用了display:none属性的元素.,所以显示之前,要额外构建一颗包含可见元素布局树
工作是,遍历dom树中占据位置的节点,把这些节点加入到布局树
不占据位置的节点被忽略,如head标签,display:none的元素
2.布局计算
现在我们有了一颗完整的布局树了,接下来,就要计算布局树节点的位置坐标了,这个过程非常复杂,先跳过,以后再将,
ps:布局操作的时候布局运算的结果会重新写回布局树中,所以布局树即使输出内容又是输入内容,这是一个不合理的地方,chrome团队正在重构这个地方的代码,提一嘴
分层
布局树出来了之后,是不是可以渲染页面了?
答案还是不能,因为现在的页面功能越来越复杂了,如复杂的3d变换,页面滚动,或者只用了z-indexing做z轴排序的,为了更方便的实现这些效果,渲染引擎还需要为特点节点生成专用的图层,并且生成一颗对应的图层树.这些图层叠加在一起构成了页面图像
想更直观的理解图层,可以在开发者工具中选择Layers标签查看分层情况
关于图层和布局树节点之间的关系,设计到一个树的层次遍历的问题(这就又扯到了数据结构了,所以数据结构一定要学好呀)
树的每一层都可以被划分为一个图层(树的层次大家自行百度)
通常情况下,如果一个节点没有对应的层,那就将他划分为他上一个节点的图层,但是不管怎么样,每个节点必然会属于一个图层.
那什么条件下,节点上的元素会被单独提升为一个层呢
第一点,拥有层叠上下文属性的元素被单独提升为一个层
什么是层叠上下文?涉及到css3中的z-index属性了,当然一般根元素html本身具有层叠上下文,成为根层叠上下文,产生一个层叠上下文一个是html标签,一个是普通元素的postition属性非static,并且设置了z-index属性产生了层叠上下,css3一些新的属性也会产生
页面本身是一个二维页面,层叠上下文能够让其拥有一个3维概念,这些html元素按照自身优先级分布垂直于这个二维平面的z轴上.一般拥有定位属性,透明度,css滤镜等属性的元素都拥有层叠上下文的属性,
第二点,需要裁剪(clip)的地方也会被创建为图层
什么是裁剪?
打个简单的比方
定义一个p标签,规定大小为200*200
然后往这个p标签里面写入大量文字,这时文字太多,超过了p标签的容量,就会出现一个小滚动条(不是浏览器最右侧的滚动条,而是p标签这个图层里的滚动条,overflow:auto)
这个就是一个图层.因为容器装不下内容,需要裁剪,形成一个图层,包含主超过的部分,产生一个滚动条
图层绘制
图层树也创建好了之后,渲染引擎才会对图层树中的每个图层进行绘制
给你一张纸让你先把背景变成蓝色,中间画一个红色的圆,然后在圆里面画一个绿色的三角形,你怎么做?
正常人都是一步步来的对吧
渲染引擎一样
把一个图层的绘制分解成一个个小指令,把这些指令按照顺序组成一个待绘制表,表里写了
让1.你先涂背景色 2.画圆 2. 画三角形
图层绘制的输出就这个待绘制表 进入下一阶段
栅格化
绘制列表只是用来记录绘制顺序和具体操作的列表,而是绘制时渲染引擎中的合成线程来完成的.
他们之间的关系是:渲染主线程负责dom树至图层绘制表的过程,然后将其结果待绘制表交给合成线程
了解栅格化之前,我们先了解什么是视口viewport
文档中可见区域叫做视口
通常一个页面很大,用户只能看到视口区域的内容,而有些情况,图层也很大,有的页面要使用滚动条滚动很久才能滚动到底部,但是通过视口,用户又只能看到页面很小的部分,这样就有很多渲染的图层被浪费了,开销很大…
基于这个原因,合成线程会将图层划分为图块,这些图块通常是256256或者512512,合成线程按照视口附近的图块优先生成位图,生成位图的操作实际栅格化来执行的.所谓栅格化就是将图块转换为位图,图块是栅格化执行的最小单位.渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内进行,通常栅格化过程会有GPU加速生成,使用GPU生成位图的过程叫做快速栅格化,或者GPU栅格化,生成的位图被保存在GPU内存中
GPU是GPU进程,这里面又涉及到了IPC,GPU进程保存图块的位图的信息
所以这个部分的输出就是,图块和位图信息.
合成和显示
一旦所有的图块都被光栅化,合成线程就会生成一个绘制图块的命令-‘drawQuad’,然后将该命令提交给浏览器进程.
浏览器进程里面有一个viz的组件,用来接收合成线程发过来的绘制命令,然后根据这个命令将页面内容绘制到内存中,最后将内存显示在屏幕上
到这里,经过一系列阶段,编写好的html,css,js就会显示出漂亮的页面了
总结
一个渲染流程大致分为以下dom,样式计算,布局树生成,分层树生成,待绘制表的生成,然后合成线程分块,栅格化,最后生成绘制命令绘制页面
- 渲染进程将html内容转换为能够读懂的DOM树结构
- 渲染引擎将css样式表转化为浏览器可以理解的StyleSheets,计算出Dom节点的样式
- 将上面二者结合生成布局树,并计算元素的布局信息
- 将布局树进行分层,生成分层树
- 为每个图层生成待绘制表,将待绘制表提交到合成线程
- 合成线程将图层分块,并且在光栅化线程池中将图块变为位图
- 合成线程生成绘制图块命令给主线程
- 主线根据命令生成页面显示到浏览器上
相关概念
1.重排概念
js或者css修改元素的几何属性,会造成布局树的改变,重新布局,这就叫重排也叫回流,这个开销是最大的,一般window窗口的改变resize和有定位的元素的位置改变会引起重排
2.重绘概念
结合渲染流程去理解这些东西,无疑是很清晰的,显然,重绘并没有改变布局树,只是改变了某个dom树节点元素本身的样式(改变了computedStyle)而已,相比于重排,省去了包括布局之后的操作,所以性能上比重排好很多
3.直接合成阶段
那我们如果去改变一个不要布局和绘制的h属性,会发生什么呢?
渲染引擎就会跳过布局和绘制,只执行后续的合成操作.
比如css的transform属性来实现动画效果,这样的话本身并不是在渲染主线程上执行操作的,没有占用主线程的资源,也不会重排重绘,所以能大大提升绘制效率(和GPU线程有关)
性能优化方案
1.触发回流和重绘的操作尽量放在一起,然后一次性更改,避免多次触发
2.虚拟dom层计算出操作的总差异,提交给浏览器,只触发一次回流或者重绘操作(这也是虚拟dom诞生的初衷之一)
最后
以上就是个性犀牛为你收集整理的浏览器渲染流程(详细)的全部内容,希望文章能够帮你解决浏览器渲染流程(详细)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复