你真的了解浏览器的渲染原理吗?
一、前言
二、解析渲染是什么
有一个场景你一定不陌生,你从浏览器的地址栏中输入了一个URL,回车之后,很快你就能看到了一个页面。那这个过程总的来说可以分为两步:
- 资源请求获取
- 解析渲染
它们的关系是这样的:
网络线程会去请求HTML文档,请求到之后会产生一个渲染的任务并加入到消息队列中,在事件循环的机制下,渲染主线程会从消息队列中取出渲染任务去执行,这时就开启了解析渲染的过程。解析渲染就是将资源请求获取到的HTML字符串最后变成可见的像素信息。
这个字符串里面包含了CSS,JS信息,因为CSS,JS都是被引入到HTML中的,如何把这些信息最后变成像素信息输出,这就是解析渲染过程。
三、解析渲染过程
1、解析HTML-Parse
(1)构建出DOM树
深度遍历DOM节点,目的是把所有DOM节点变成一个JS对象,使得JS可以去操作DOM。
(2)构建CSSOM
把包含的所有的CSS转化成CSSOM树,目的同样是把所有的CSS变成一个对象,使JS可以去操作。HTML中的CSS可能包含以下几种类型:
<style>
内部样式表
<link>
外部样式表<div style="">
行内样式
- 浏览器默认样式表
经过转化后,每一种类型都是一个对象,随便打开一个页面,控制台可以查看所有样式对象化后的结果:
具体是怎么转化的:
(3)CSS,JS资源请求
HTML解析过程会构建DOM树和CSSOM树,那这个过程是怎么样的呢?
渲染主线程在解析HTML过程中,为了提高效率,会同时启动一个预解析线程,目的是让预解析线程率先把HTML包含的外部的CSS,JS文件下载。
如果主线程遇到link位置,此时外部的CSS文件还没有下载解析好,主线程不会等待,继续解析后续的HTML。如果后面预解析线程解析好了CSS,把结果交给主线程,主线程转化成CSSOM。由于下载和解析CSS的工作是在预解析线程中进行的,因此CSS不会阻塞HTML的解析。
如果主线程解析遇到script位置,会停止解析HTML,等待JS文件,并将全局代码解析执行完成后,才能继续解析HTML。因为JS代码执行过程中可能会修改当前的DOM树,所以DOM树的生成必须要暂停,因此JS的执行会阻塞HTML的解析。
如果JS执行过程中,有引用CSS文件的,此时CSS还没有下载好,此时JS执行又会阻塞,必须等CSS都下载好了才会继续执行。
基于这个原因,在实际的开发中,CSS的引入放在HTML的head中,JS脚本代码放在HTML尾部,可以很大程度避免因为引入资源的位置不当导致首次渲染时间过长。
2、样式计算-Style
主线程会遍历得到的DOM树,依次为树中的每个节点计算出它最终的样式,Computed Style。这个过程会得到一棵带有样式的DOM树。
3、布局-Layout
依次遍历DOM树的每一个节点,计算每个节点的几何信息。例如节点的宽高,相对包含块的位置。大部分情况,DOM树和布局之后的布局树并不是一一对应的。
比如display: none的节点没有几何信息,因此不会生成到布局树中;使用了伪元素选择器,虽然DOM树中不存在这些伪元素节点,但如果他们拥有几何信息,也会被生成到布局树中。
该过程之后会得到一些新的属性,比如:dom.clientWidth
4、分层-Layer
主线程会使用一套复杂的策略对整个布局树进行分层。这样做的目的是,将来某一个层改变后,仅仅对该层进行后续处理,提升效率。
滚动条、堆叠上下文、transform、opacity等样式会影响分层的结果。
5、绘制-Paint
主线程会为每个层单独产生绘制指令集,用于描述这一层的内容应该如何画出来。
6、分块-Tiling
完成绘制后,主线程将每个图层的绘制信息提交给合成线程,剩余工作让合成线程完成。合成线程对每个层进行划分成多个区块。
7、光栅化-Raster
这个阶段是由GPU进程来完成,GPU进程会开启多个线程完成光栅化,并且优先处理靠近视口区域的块。光栅化的结果会得到一块块位图,交回给合成线程。
8、画-Draw
合成线程拿到每个层、块的位图之后,生成一个指引信息。指引信息标识出每个位图应该画在屏幕哪个位置,以及会考虑旋转,缩放等变形。变形发生在合成线程,与渲染主线程无关,这就是transform效率高的本质原因。
合成线程会把指引信息交个GPU进程,由GPU进程产生系统调用,提交给GPU硬件比如显卡,最终完成显示。
四、什么是回流(reflow)
涉及到页面布局的几何属性的改变会引发回流 ,几何属性的改变需要重新计算布局树,会引发layout,进而导致引发后续所有的过程。
如果我们使用JS连续多次修改几何属性,因为JS的执行和渲染是互斥,如果在JS修改了某个几何属性之后想立马获取该属性值,可能会造成获取不到最新的值。
为了避免这个问题,浏览器最终决定:
JS获取布局树属性会立即执行reflow,然后返回最新的值
dom.style.width = xx
dom.style.height = xx
dom.margin = xx
dom.clientWidth = xx
五、什么是重绘(repaint)
当修改了可见样式相关的属性,会重新生成绘制指令,进而引发后续过程。
由于元素的布局信息也是属于可见样式,所以reflow一定会引起repaint
转载自:https://juejin.cn/post/7224794158744748088