浏览器渲染原理简析

(参考文章)

线程与进程的概念

进程是并行的,在同一个时刻,多个进程相互独立执行。进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。

线程是并发的,表现在在宏观上同步进行,微观上交替执行。在某一时刻,只有一个线程会执行。线程是处理机的调度单位,在多CPU计算机中,各个线程可占用不同的 CPU。由于共享内存空间,同一进程中的线程通信无需系统干预。

较老的浏览器采用的是单进程架构,现代浏览器一般采用多进程架构,现在主流浏览器的多进程架构为:

  • 主进程
  • 渲染进程
  • 插件进程
  • GPU进程
  • 网络进程

浏览器的线程

GUI渲染线程:渲染浏览器页面,解析HTML,CSS,构建DOM树和Rander树,当触发重绘或回流时,该线程就会执行\

JavaScript引擎线程:执行JavaScript脚本并运行JavaScript代码,无论什么时候都只有一个JavaScript线程在执行JavaScript代码,注意JavaScript线程跟GUI线程都必须作为主线程运行,因此这两个线程是互斥的,过多地用JavaScript操作DOM理论上会出现阻塞,导致渲染不连贯。

事件触发线程:当有事件触发时,会将事件推入一个队列,然后等待JavaScript引擎按顺序执行处理,由于JavaScript是单线程的,因此这些事件会按队列排队执行\

定时器线程:setTimeout和setInterval在该线程中进行处理,由于JavaScript是单线程的,为了防止阻塞对定时器产生的误差,定时器和触发器都会单独采用一个线程进行处理\

http线程:这个线程用于处理网络请求,在XMLHttpRequest连接后出现。这个线程会检测请求中的状态变更。如果设置了回调函数,会将回调函数放到事件线程中等待处理\

浏览器渲染的流程

整个HTML文件从上往下顺序解析,解析到link标签或有外部引入内容的src后会请求外部资源.如果有外部引入的CSS文件,会对其解析并构建CSSOM,解析CSSOM和生成DOM树的过程是可以同步进行的,因此最好将CSS的引入写在HTML文件的上方,让他们同步解析。
在现代浏览器中,一般有一个预处理的机制,会进行第一遍快速扫描,找到需要从外部引入的文件,在解析DOM树之前就开始请求所有外部资源。

由于JavaScript可能会操作DOM,导致已经解析好的DOM被打乱需要重新解析,因此当解析到script标签后,会暂停html的解析,而立即解析JavaScript代码。因此,为了能让我们尽快看到网站的页面,我们应该将script标签写在html的尾部。或者添加属性defer或async,让JavaScript代码等待html解析完再进行解析

处理HTML并构建Dom树\

五个过程:

字节 字符 token node dom\

四个步骤:

  • 字符转化:浏览器从磁盘或网络中读取到html的原始字节,根据文件的指定编码(utf-8)将他们转换成各个字符\
  • 令牌(token)化: 将解析出的字符串转化为HTML5标准规定的各种令牌,比如各种标签,每个标签拥有特殊的含义和规则。
    例如: startTag:html startTag:body startTag div hello endTag div … endTag html
  • 词法分析:根据token的属性和解析规则,将这些token解析成指定的对象(节点)
  • Dom构建:由于html中定义了不同元素的关系(父子关系,兄弟关系等),在这一步,要对这些关系进行构建,确定各个元素节点的关系结构,这部分需要使用建树算法
    ###处理CSS并构建CSSOM
  • 格式化:将CSS从0,1的二进制文件解析成浏览器能够识别的styleSheets(样式表)\
  • 标准化:在CSS中,有很多相对单位,比如%,em,bolder…对于这些单位,在标准化的过程中会统一转化为标准单位px,对于颜色,red,blue这种写法,和八进制写法会统一转化为rgba表示法\
  • 计算:CSS的计算规则:层叠,继承,层叠是指CSS会用一些方法处理从多个地方得到的值,比如相同属性,选择器权重高的覆盖权重低的,后来的覆盖之前的等等。继承是值一些CSS属性是可以被继承的,比如宽度,对于块级元素,如果不给子元素设置宽度,那么子元素会继承父元素的宽度,如果这个宽度不是我们希望看到的,我们可以重写子元素的宽度。。CSS有些属性是可继承的,有些属性是不可继承的。\

将DOM树和CSSOM树合并为渲染(rander)树\

将之前构建的Dom树和CSSOM树合并,这一步中确定了各个节点的基本样式,整个解析路线大致如下:

  • 拿到DOM树和样式规则
  • filtering:对应该用到页面上的规则用以下条件筛选(包括选择器匹配,属性和状态是否有效,media),查找到每一个元素当前对应属性的样式表,此时样式表中的每个值是声明值
  • cascading(层叠):按照来源。!important,选择器特异性,书写顺序选择出优先级最高的一个属性值,这个阶段拿到的值叫做层叠值
  • defaulting:当层叠值为空时,使用继承或初始值,这个阶段的值交指定值,这个阶段的值一定不为空
  • resolving:对一些相对值或关键字转化为绝对值(比如em转化为px,相对路径转化为绝对路径),这个阶段拿到的值叫做计算值,一般来说,计算值是浏览器在不进行布局时,能拿到的最具体的值。注意,类似于width:60%这类值,计算值是不会转化的,因为如果不进行布局,就无法知道具体宽度。
  • formatting:对计算值进行进一步计算,关键字,百分比都会被转化为绝对值,这个步骤得到的使用值,是进行实际布局时使用的值,不会再有相对值或关键字。
  • constraining:将有小数点的像素值转化为整数,对超出min-width或max-width的值进行处理,对于浏览器渲染的最小字体进行规范…最后限制后得到实际值

根据渲染树来布局,计算每个节点的位置,生成布局(layout)树

在DOM树中,包含了一些display:none;meta这些不可见的元素,因此浏览器会重新构建一颗仅包含可见元素的树,称为布局树,这一步大体分为一下两部分:

  • 遍历DOM树中的所有可见节点,将这些节点加到布局树当中\
  • 忽略所有不可见的元素,包括,display:none的元素\

之后,浏览器内核会进行一系列非常复杂的操作,计算各个元素的宽高,确定各个元素在浏览器上显示出来的位置\

在这个过程中,浏览器还会确定各个元素的图层和层级,构建图层树,这部分的详细内容下面会指出.\

GUI绘制

浏览器会根据之前构建的图层树,确定一系列的绘制指令,根据这些构建指令去绘制页面,通常一个元素要用多条指令进行绘制,因为背景,边框都需要专门的指令进行绘制,GUI线程会专门调用一个叫做合成线程的子线程.主线程(此时应为GUI线程)会将绘制指令都提交给合成线程.\

确定了绘制指令后,浏览器会调用操作系统的GPU去执行绘制操作,这里浏览器会将图层划分为图块来解决由于滚动条导致的图层很大,但可视区域不大的问题…谷歌为了保证首屏的加载速度,还采用了第一次采用低分辨率图片,之后正常图块绘制完毕后,再将低分辨率图块替换的策略.\

位图,亦称为点阵图像或栅格图像,是由称作像素(图片元素)的单个点组成的

有了图块后,合成线程会按照视口附近的图块来优先生成位图(栅格化),渲染进程中专门维护一个栅格化线程池,负责把图块转换成位图数据,合成线程会选择视口附近的图块送往栅格化线程池,将他们生成位图.生成位图的过程中会依靠GPU进行加速,生成的位图最后会发送给合成线程\

栅格化完成后,合成线程会生成一个绘制命令,并发送给浏览器进程.浏览器进程中的viz组件收到这个命令后,根据这个命令将页面绘制到内存中,然后将这部分页面发送到显卡.也就能够显示出页面了.显卡分为前缓冲区和后缓冲区,这两部分不断地相互交换,使屏幕的内容不断变化.形成了动画效果.

重绘与回流,图层问题

重绘:

页面元素的样式改变,但并不影响它在文档流中的位置,浏览器会将新样式赋给他并重新绘制.更改color,visibility属性会触发重绘\

###回流(重排):
对DOM结构,样式的修改引发了DOM几何尺寸,位置的改变,会触发回流,回流会重新绘制DOM树,重新执行之后的全部流程触发回流的情况包括:

  • DOM元素的几何属性变化,包括width,padding,left等
  • DOM元素的增减,移动
  • 读写offset,scoll等属性
  • 调用window.getComputedstyle方法

重绘的成本远低于回流,因此我们尽可能少地触发回流.比如:\

  • 当要隐藏元素时,用修改visiblity的方法来代替修改display.
  • 当要修改一个父元素中的大量子元素时,可以进行离线处理,先将父元素的display设为none,再处理子元素,这样仅触发两次回流.
  • 尽量不要将offset,scoll这些属性直接放在循环内部进行处理,因为每次读取都会触发回流

合成

当对于一个既不需要布局也不需要绘制的属性,渲染流程会跳过布局和绘制的操作,直接执行后面的合成操作,比如CSS中的transfrom实现动画,就避免了重绘和回流,直接在非主线程上执行合成的操作,大大提高效率.同时合成的位图会由GPU进行处理,比CPU处理的效率要高.\

可以进行合成的CSS属性:

  • transfrom
  • opacity
  • filter

图层:

一些属性可以让元素称为一个单独的图层,图层能够阻止该节点的回流影响其他元素,

能够产生图层的方法有:

  • 设置了position属性并且设置了z-index
  • opacity的值不为1
  • transfrom的值不为none
  • filter的值不为none
  • 设置will-change属性
  • 使用video标签的节点
  • 出现了剪裁或滚动条的情况

注意:当z-index有比较低的数值但提升为一个独立的图层,那么z-index比他高的节点都会被提升为一个独立的图层,这时可能出现层爆炸的情况,浪费大量内存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值