前言浏览器的内核是指支持浏览器运行的核心程序。它分为两部分,一个是渲染引擎,一个是JS引擎。渲染引擎在不同的浏览器中并不完全相同。目前市面上常见的浏览器内核可以分为四种:Trident(IE)、Gecko(Firefox)、Blink(Chrome、Opera)、Webkit(Safari)。这里大家最熟悉的可能就是Webkit内核了,它才是当前浏览器界真正的霸主。在本文中,我们以Webkit为例,对现代浏览器的渲染过程进行深入分析。想阅读更多优质文章,请戳GitHub博客,一年五十篇优质文章等你来!页面加载过程在介绍浏览器渲染过程之前,我们简单介绍一下页面加载过程,以帮助大家更好地理解后续的渲染过程。要点如下:浏览器从DNS服务器获取域名的IP地址,并向拥有该IP的机器发送HTTP请求。服务器接收、处理并返回HTTP请求。浏览器获取返回的内容。例如在浏览器中输入https://juejin.im/timeline,经过DNS解析,juejin.im对应的IP为36.248.217.149(不同时间不同地点对应的IP可能不同)。然后浏览器向该IP发送HTTP请求。服务端收到HTTP请求,经过计算(给不同的用户推送不同的内容),返回HTTP请求。返回内容如下:其实就是一堆HMTL格式的字符串,因为只有HTML格式的浏览器才能正确解析。这是W3C标准的要求。下一步是浏览器的渲染过程。浏览器渲染过程浏览器渲染过程大致分为以下三个部分:1)浏览器会解析三样东西:一个是HTML/SVG/XHTML,HTML字符串描述了一个页面的结构,浏览器将HTML结构字符String解析转换DOM树结构。第二个是CSS。解析CSS会生成一个CSS规则树,它更类似于DOM结构。第三个是Javascript脚本。加载Javascript脚本文件后,通过DOMAPI和CSSOMAPI操作DOMTree和CSSRuleTree。2)解析完成后,浏览器引擎会通过DOMTree和CSSRuleTree构建RenderingTree。渲染树渲染树不等同于DOM树。渲染树只包括需要展示的节点和这些节点的样式信息。CSS的RuleTree主要是完成匹配,将CSSRule附加到RenderingTree上的每个Element(也就是每个Frame)上。然后,计算每个Frame的位置,也称为布局和回流过程。3)最后调用操作系统NativeGUI的API进行绘制。接下来,我们将详细说明我们所经历的重要步骤。构建DOM浏览器将遵循一组将HTML文件转换为DOM树的步骤。宏观上可以分为几个步骤:浏览器从磁盘或网络中读取HTML的原始字节,按照文件指定的编码(如UTF-8)转换成字符串。网络中传输的内容实际上是0和1字节的数据。当浏览器接收到这些字节数据后,会将这些字节数据转换成字符串,也就是我们写的代码。将字符串转换为token,例如:、
等。Token会标识当前Token是“开始标签”还是“结束标签”或“文本”等信息。这时候你肯定会有疑惑,如何维护节点之间的关系呢?其实这就是Token识别“开始标签”和“结束标签”的作用。例如,“title”Token的开始标签和结束标签之间的节点必须属于“head”的子节点。上图展示了节点之间的关系。例如,“Hello”Token位于“title”开始标签和“title”结束标签之间,说明“Hello”Token是“title”Token的子节点。同样,“title”Token是“head”Token的子节点。生成节点对象和构建DOM实际上,在构建DOM的过程中,不是等待所有的Token转换完毕再生成节点对象,而是在生成Tokens的同时消耗Tokens来生成节点对象。也就是说,每一个Token生成后,都会立即消耗这个Token,创建一个节点对象。注意:带有结束标记的Token不会创建节点对象。下面我们举个例子,假设有一段HTML文本:
网页解析上面的HTML将被解析如下:构建CSSOMDOM将捕获页面的内容,但是浏览器还需要知道页面如何显示,所以需要构建CSSOM。构建CSSOM的过程与构建DOM的过程非常相似。当浏览器收到一段CSS后,浏览器首先要做的就是识别Token,然后构建节点,生成CSSOM。在这个过程中,浏览器会判断每个节点的样式是什么,这个过程其实是非常耗资源的。因为样式可以自己设置到一个节点上,也可以通过继承获取。在此过程中,浏览器必须递归CSSOM树以确定特定元素的外观。注意:CSS匹配HTML元素是一件相当复杂且对性能至关重要的事情。所以DOM树要小,CSS尽量使用id和class,不要过渡到级联。构建渲染树当我们生成DOM树和CSSOM树时,我们需要将这两棵树组合成一棵渲染树。在这个过程中,并不是两者的简单结合。渲染树只会包含需要展示的节点和这些节点的样式信息。如果一个节点是display:none,它就不会显示在渲染树中。我们可能会有疑问:浏览器在渲染过程中遇到JS文件怎么办?在渲染过程中,如果遇到没有defer或者async,浏览器会立即加载并执行指定的脚本,也就是说不等待后续加载,直接加载文档元素并执行。2)情况2(异步下载)async属性表示引入的JavaScript是异步执行的。这是HTML解析阶段还是在DOMContentLoaded触发之后。需要注意的是,以这种方式加载的JavaScript仍然会阻塞load事件。也就是说,async-script可以在DOMContentLoaded触发之前或之后执行,但必须在load触发之前执行。3)案例3(延迟执行)defer属性表示导入的JavaScript延迟执行,即加载这段JavaScript时HTML不停止解析,并且这两个过程是并行的。在解析完整个文档并加载defer-script之后(这两件事的顺序无关紧要),将执行defer-script加载的所有JavaScript代码,然后触发DOMContentLoaded事件。defer与普通脚本相比有两个不同:**加载JavaScript文件时,不阻塞HTML解析,将执行阶段放在HTML标签解析完成之后。加载多个JS脚本时,async是乱序加载的,而defer是顺序加载的。**2。为什么DOM运行起来很慢?将DOM和JavaScript想象成一座孤岛,它们通过收费桥相连。——《高性能 JavaScript》JS非常快,在JS中修改DOM对象也非常快。在JS的世界里,一切都简单而快速。但DOM操作并不是JS的独舞,而是两个模块之间的协作。因为DOM是渲染引擎里面的东西,而JS是JS引擎里面的东西。当我们使用JS来操作DOM时,本质上是JS引擎和渲染引擎之间的“跨界通信”。这种“跨界通信”的实现并不简单,它依赖桥接接口作为“桥梁”(如下图)。过“桥”是要付出代价的——成本本身是不容忽视的。每次我们操作DOM(无论是修改它还是只是访问它的值)时,我们都必须经过一个“桥梁”。如果越过“桥”的次数越多,性能问题就越明显。因此,“减少DOM操作”的建议并非空穴来风。3、你真的了解回流和重绘吗?渲染过程基本是这样的(如下图黄色的四个步骤):1.计算CSS样式2.构建RenderTree3.Layout——定位坐标和尺寸4.正式开启注意:里面有很多连接线上图,意思是Javascript动态修改DOM属性或者CSS属性会导致重新布局,但是有的变化不会重新布局,就是上图中指向天空的箭头,比如修改后的CSS规则与元素不匹配。这里要讲两个重要的概念,一个是Reflow,一个是Repaint重绘:当我们对DOM的修改导致样式改变,但不影响它的几何属性(比如修改颜色或者背景色)时,浏览controller不需要重新计算元素的几何属性,直接为元素绘制新的样式(跳过上图的reflow环节)。Reflow:当我们修改DOM导致DOM的几何尺寸发生变化时(比如修改元素的宽高或者隐藏元素等),浏览器需要重新计算元素的几何属性(the其他元素的几何属性和位置也会受到相应的影响(受影响),然后绘制计算结果。这个过程就是回流(也叫重排)。我们知道,一个网页在生成时,至少会被渲染一次。在用户访问期间,会不断重新渲染。重新渲染会重复回流+重绘或者只重绘。回流必然引起重绘,重绘不一定引起回流。我们在设置节点样式的时候会经常出现repainting和reflow,对性能也会有很大的影响。回流的代价远高于重绘,改变父节点中的子节点很可能会引起父节点的一系列回流。1)引起回流的常见属性和方法任何改变元素几何信息(元素的位置和大小)的操作都会触发回流,增加或删除可见的DOM元素;元素大小变化——margin、padding、border、width和height内容发生变化,比如用户在输入框输入文本,浏览器窗口大小发生变化——当resize事件发生时,计算offsetWidth和offsetHeight属性来设置style属性的取值2)引起重绘的常见属性和方法3)如何减少回流和回流Draw用transform代替top用visibility代替display:none,因为前者只会引起重绘,后者会引起回流(更改布局)。不要将循环中节点的属性值作为循环中的变量。for(leti=0;i<1000;i++){//获取offsetTop会导致回流,因为需要获取正确的值console.log(document.querySelector('.test').style.offsetTop)}Do不使用Table布局,一个小的改变可能会导致选择整个table的重新布局动画执行的速度。动画速度越快,回流次数越多。也可以选择使用requestAnimationFrameCSS选择器从右到左进行匹配搜索,避免节点层数过多,将频繁重绘或回流的节点设置为层。该层可以防止节点的渲染行为影响其他节点。例如,对于视频标签,浏览器会自动将节点变成图层。性能优化策略是基于上述浏览器渲染原理,DOM和CSSOM结构的构建顺序,初始化可以优化页面渲染,提高页面性能。JS优化: