You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Blink: 由Google和Opera Software开发的浏览器排版引擎,在Chrome(28及往后版本)、Opera(15及往后版本)和Yandex浏览器 中使用。Blink 其实是 WebKit 的一个分支,添加了一些优化的新特性,例如跨进程的 iframe,将 DOM 移入 JavaScript 中来提高 JavaScript 对 DOM 的访问速度等,目前较多的移动端应用内嵌的浏览器内核也渐渐开始采用 Blink。
渲染引擎的工作流程
浏览器渲染引擎重要的工作就是将 HTML 和 CSS 文档解析组合最终渲染到浏览器窗口上。如下图所示,渲染引擎在接受到 HTML 文件后主要进行了以下操作:解析 HTML 构建 DOM 树 -> 构建渲染树 -> 渲染树布局 -> 渲染树绘制。
解析 HTML 构建 DOM 树时渲染引擎会将 HTML 文件的便签元素解析成多个 DOM 元素对象节点,并且将这些对象根据父子关系组成一个树结构。同时 CSS 文件被解析成 CSS规则表,然后将每条 CSS 规则按照「从右向左」的方式在 DOM 树上进行逆向匹配,生成一个具有样式规则描述的 DOM 渲染树。接下来就是将渲染树进行布局、绘制的过程。首先根据 DOM 渲染树上的样式规则,对 DOM 元素进行大小和位置的定位,关键属性如position;width;margin;padding;top;border;...,接下来在根据元素样式规则中的color;background;shadow;...规则进行 DOM 的绘制。
再者,需要注意的是,在浏览器渲染完首屏页面后,如果对 DOM 进行操作会引起浏览器引擎对 DOM 渲染树的重新布局和重新绘制,我们叫做「重排」和「重绘」,由于重排和重绘是前后依赖的关系,所以重绘发生时未必会触发渲染引擎的重排,但是如果发生了重排就必然会触发重绘操作,这样带来的性能损害就是巨大的。因此我们在做性能优化的时候应该遵循「避免重排;减少重绘」的原则。
不同浏览器内核间的差异
在不同的浏览器内核下, 浏览器页面渲染的流程略有不同
上面两幅图分别是 Webkit 和 Geoko 内核渲染 DOM 的工作流程,对比可以看出,两者流程的区别主要在于 CSS 样式表的解析时机,Webkit 内核下,HTML 和 CSS 文件的解析是同步的,而 Geoko 内核下,CSS 文件需要等到 HTML 文件解析成内容 Sink 后才进行解析。
上面我们提到过, CSS 规则是按照「从右向左」的方式在 DOM 树上进行逆向匹配的,最终生成一个具有样式规则描述的 DOM 渲染树。
但是你知道为什么要「从右向左」做逆向匹配码?
我们重新回到【webkit 内核工作流程】图
CSS 规则匹配是发生在webkit引擎的「Attachment」过程中,浏览器要为每个 DOM Tree 中的元素扩充 CSS 样式规则(匹配 Style Rules)。对于每个 DOM 元素,必须在所有 Style Rules 中找到符合的 selector 并将对应的规则进行合并。选择器的「解析」实际是在这里执行的,在遍历 DOM Tree 时,从 Style Rules 中去寻找对应的 selector。
我们在面试的时候或许经常会被问到:
确实是个老生常谈的问题,但问题的答案并不是唯一的,或许在三五年前,这个问题还会有一个「相对」标准的答案。
此答案精简的概括了「后端为主的 MVC 模式」及早期 Web 应用的浏览器相应的全过程。那,前端技术发展到现在,「前后端分离」「中间件直出」和「MNV*模式」也已问世,再谈及此问题,答案会有不同。
就以「前后端分离」为例,在上方答案的第4步后,紧接着就不会直接进入后端服务器了。而会被 HTTP 和反向代理服务器,如 Ngnix,代替。
以上步骤可以发现,浏览器可能会触发重绘两次,极易发生「白屏」或「页面抖动」,为了解决这个问题「中间件直出」的模式应运而生。另外为了扩充大前端的阵营,吸纳 IOS 和 Android,Google又设计了「MNV*模式」,典型代表就是 ReactNative,但此模式已经脱离了浏览器的范畴,此处就不再做扩展。
以上讨论的渲染过程中使用到了较多的浏览器功能,如用户地址栏输入框、网络请求、浏览器文档解析、渲染引擎渲染网页、 JavaScript 引擎执行js脚本、客户端存储等。 接下来我们介绍下浏览器的基本结构组成。
浏览器的结构组成
浏览器一般由七个模块组成,User Interface(用户界面)、Browser engine(浏览器引擎)、Rendering engine(渲染引擎)、Networking(网络)、JavaScript Interpreter(js解释器)、UI Backend(UI 后端)、Date Persistence(数据持久化存储) 如下图:
作为前端开发人员,我们需要重点理解渲染引擎的工作原理,灵活应用数据存储技术,在实际项目开发中会经常涉及到这两个部分,尤其是在做项目性能优化时,理解浏览器渲染引擎尤为重要。而其他部分则是由各种浏览器自行管理的,开发者能控制的地方较少。今天我们就围绕这两个重点其中的一个部分「浏览器渲染引擎」进行展开
浏览器渲染引擎
浏览器渲染引擎是由各大浏览器厂商依照 W3C 标准自行实现的,也被称之为「浏览器内核」。
目前,市面上使用的主流浏览器内核有5类:Trident、Gecko、Presto、Webkit、Blink。
Trident:俗称 IE 内核,也被叫做 MSHTML 引擎,目前在使用的浏览器为 IE11-,以及各种国产多核浏览器中的IE兼容模式。另外Edge 浏览器不再使用 MSHTML 引擎,而是使用类全新的引擎 EdgeHTML。
Gecko:俗称 Firefox 内核,Netscape6开始采用的内核,后来的Mozilla FireFox(火狐浏览器) 也采用了该内核,Gecko的特点是代码完全公开,因此,其可开发程度很高,全世界的程序员都可以为其编写代码,增加功能。因为这是个开源内核,因此受到许多人的青睐,Gecko内核的浏览器也很多,这也是Gecko内核虽然年轻但市场占有率能够迅速提高的重要原因。
Presto:Opera 前内核,为啥说是前内核呢?因为 Opera12.17 以后便拥抱了 Google Chrome 的 Blink 内核,此内核就没了寄托
Webkit:Safari 内核也是 Chrome 内核原型,主要是 Safari 浏览器在使用的内核,也是特性上表现较好的浏览器内核。也被大量使用在移动端浏览器上。
Blink: 由Google和Opera Software开发的浏览器排版引擎,在Chrome(28及往后版本)、Opera(15及往后版本)和Yandex浏览器 中使用。Blink 其实是 WebKit 的一个分支,添加了一些优化的新特性,例如跨进程的 iframe,将 DOM 移入 JavaScript 中来提高 JavaScript 对 DOM 的访问速度等,目前较多的移动端应用内嵌的浏览器内核也渐渐开始采用 Blink。
渲染引擎的工作流程
浏览器渲染引擎重要的工作就是将 HTML 和 CSS 文档解析组合最终渲染到浏览器窗口上。如下图所示,渲染引擎在接受到 HTML 文件后主要进行了以下操作:解析 HTML 构建 DOM 树 -> 构建渲染树 -> 渲染树布局 -> 渲染树绘制。
解析 HTML 构建 DOM 树时渲染引擎会将 HTML 文件的便签元素解析成多个 DOM 元素对象节点,并且将这些对象根据父子关系组成一个树结构。同时 CSS 文件被解析成 CSS规则表,然后将每条 CSS 规则按照「从右向左」的方式在 DOM 树上进行逆向匹配,生成一个具有样式规则描述的 DOM 渲染树。接下来就是将渲染树进行布局、绘制的过程。首先根据 DOM 渲染树上的样式规则,对 DOM 元素进行大小和位置的定位,关键属性如
position;width;margin;padding;top;border;...
,接下来在根据元素样式规则中的color;background;shadow;...
规则进行 DOM 的绘制。另外,这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
再者,需要注意的是,在浏览器渲染完首屏页面后,如果对 DOM 进行操作会引起浏览器引擎对 DOM 渲染树的重新布局和重新绘制,我们叫做「重排」和「重绘」,由于重排和重绘是前后依赖的关系,所以重绘发生时未必会触发渲染引擎的重排,但是如果发生了重排就必然会触发重绘操作,这样带来的性能损害就是巨大的。因此我们在做性能优化的时候应该遵循「避免重排;减少重绘」的原则。
不同浏览器内核间的差异
在不同的浏览器内核下, 浏览器页面渲染的流程略有不同
上面两幅图分别是 Webkit 和 Geoko 内核渲染 DOM 的工作流程,对比可以看出,两者流程的区别主要在于 CSS 样式表的解析时机,Webkit 内核下,HTML 和 CSS 文件的解析是同步的,而 Geoko 内核下,CSS 文件需要等到 HTML 文件解析成内容 Sink 后才进行解析。
另外两者的不同还有描述术语,除此之外两者的流程就基本相同了,其中最重要的三个部分就是 「HTML 的解析」「CSS 的解析」「渲染树的生成」。这三个部分的原理内容就比较深,涉及到「词法分析」「语法分析」「转换」「解释」等数据结构的内容,比较枯燥,一般我们了解到这里就够了,不过想深入了解的同学可以阅读此篇译文,浏览器的工作原理,里面详细的解释了以上三个部分的流程和关系。此处就不再多做赘述了。
关于 CSS 规则的匹配
上面我们提到过, CSS 规则是按照「从右向左」的方式在 DOM 树上进行逆向匹配的,最终生成一个具有样式规则描述的 DOM 渲染树。
但是你知道为什么要「从右向左」做逆向匹配码?
我们重新回到【webkit 内核工作流程】图
CSS 规则匹配是发生在webkit引擎的「Attachment」过程中,浏览器要为每个 DOM Tree 中的元素扩充 CSS 样式规则(匹配 Style Rules)。对于每个 DOM 元素,必须在所有 Style Rules 中找到符合的 selector 并将对应的规则进行合并。选择器的「解析」实际是在这里执行的,在遍历 DOM Tree 时,从 Style Rules 中去寻找对应的 selector。
我们来举一个最简单的栗子:
此处我们有一个 html 元素 和一个 style 元素,两者需要做遍历匹配
此处会有 4*3 个匹配项,如果做正向匹配,在遇到
<span>
标签匹配div .t p{ color: red; }
到匹配项时,显然时不通过到,计算机首先要找到<span>
标签到父标签和祖父标签,判断他们是否满足div .t
的规则,然后在匹配<span>
是否为p
标签,此处匹配不成功,此处就产生了三次浪费。如果时逆向匹配,那么第一次对比
<span>
是否为p
标签便可排除此规则,效率更高。如果将 HTML 结构变复杂,CSS 规则表变庞大,那么,「逆向匹配」的优势就远大于「正向匹配」了,因为匹配的情况远远低于不匹配的情况。同时如果在选择器结尾加上通配符「*」,那么「逆向匹配」的优势就大打折扣,这也就是很多优化原则提到的尽量避免在选择器末尾添加通配符的原因。
极限了想,如果我们的样式表不存在嵌套关系,如下:
那么引擎的「Attachment」过程将得到极大的精简,效率也是可想而知的,这就是为什么「微信小程序」样式表不建议使用关系行写法的原因。
相关的性能优化
由以上介绍,我们大致可以在案例中看到同浏览器渲染引擎相关的可行优化点。
大致为以下几种
The text was updated successfully, but these errors were encountered: