浏览器简介
ᅟᅠᅟᅠ浏览器(英语:Web Browser,全称为网页浏览器)是一种用于检索并展示Web信息资源的应用程序。这些信息资源可为网页、图片、影音或其他内容,它们由统一资源标志符(URI)标识。信息资源中的超链接可使用户方便地切换并浏览其他网页的信息。浏览器是Web网页的载体,是查找网上资源的入口和途径。
ᅟᅠᅟᅠ浏览器是一个非常复杂的应用程序。对于前端开发者来说,浏览器的工作是根据不同的IP和端口,查找并解析HTML内容,并将HTML中编写的代码渲染成可视的网页。如果网页需要交互,浏览器还要执行JavaScript脚本,这是现代浏览器的核心。当然使用浏览器检索资源时还离不开网络、搜索引擎、数据存储等,看似简单的搜索和打开网页,其实中间经历了非常复杂的过程。
ᅟᅠᅟᅠ深入了解浏览器这个庞然大物的最好方式就是将其拆分开来。我们把浏览器的每个组成部分看成一个独立单元,详细了解每一个单元的功能和作用是什么,进而了解它的工作原理和运行机制。
1. 浏览器组成
浏览器的组成基本上可以分为几大结构,我们从如图所示的结构图开始。 上图中将浏览器结构分为了 8 个部分,每个部分对应的含义如下:
User Interface
:用户界面。Browser Engine
:浏览器引擎。Rendering Engine
:渲染引擎。JavaScript Interpreter
:JavaScript解释器。Networking
:网络模块。XML Parser
:XML解析器。Display Backend
:UI后台。Data Persistence
:数据存储。
上面列出的每个部分都负责浏览器中的一块独立的内容。比如:Networking
负责整个浏览器的资源请求;XML Parser
用于将XML文档解析成DOM树;Data Persistence
负责浏览器中的所有数据存储,如localStorage、sessionStorage、cookie等。
为了更好的理解浏览器的工作原理,我们从上述的8个模块中选择最核心的4个模块详细介绍,这4个模块按照执行顺序依次是:用户界面、浏览器引擎、渲染引擎、JavaScript引擎(解释器)。
1.1 用户界面
ᅟᅠᅟᅠ用户界面就是用户肉眼可见的浏览器界面,包括浏览器的工具栏、地址栏、标签页、网页页面等。当我们通过鼠标或者键盘在用户界面上执行一些操作时,这些动作的本质是在向浏览器下达一个个"执行命令"。而用户界面的作用就是将用户的交互动作转换为命令,然后将这些命令交给浏览器引擎处理。
1.2 浏览器引擎
ᅟᅠᅟᅠ浏览器引擎接收到用户界面传来的命令后,它可以读懂这些命令的内容。虽然浏览器引擎认识命令,但它不会执行命令,它的作用依然是"转换命令" ——— 将命令转换为渲染引擎能理解的方式并将其传递。可以看出,用户界面和浏览器引擎只负责解析命令,但不会执行命令,它们都相当于是浏览器操作的"翻译员"和"搬运工"。
最终,经过两层的转换和翻译,命令终于被传到了渲染引擎的手中并开始执行。
1.3 渲染引擎
ᅟᅠᅟᅠ渲染引擎的工作就是将静态资源渲染为可视化界面,或者在用户操作后将页面及时更新并渲染。可以说,"渲染页面"就是渲染引擎的主要职责。渲染引擎可以识别我们的HTML、CSS代码,并利用这些代码信息计算页面布局,最终将代码变成丰富多样的页面显示在浏览器窗口上。
除此之外,渲染引擎还可以调度其他的工作模块。假设用户点击了一个按钮,该按钮点击后会异步请求一个文本资源,并将其存储在localStorage中,此时的渲染引擎并没有执行页面渲染的操作,却分别调用了网络模块(Networking)和数据存储(Data Persistence)的功能。这表示渲染引擎不仅可以执行UI更新的操作,也可以执行其他非页面变化的逻辑任务。
因为渲染引擎是整个浏览器的核心,因此人们把渲染引擎也看作是"浏览器内核"。
1.4 JavaScript引擎
ᅟᅠᅟᅠJavaScript是一种解释型的语言,其解释器被称作"JavaScript引擎"。JavaScript引擎的作用就是执行JavaScript代码。其原理主要包含如下4个步骤:
- 将JavaScript代码拆分为最小的单个字符。
- 将字符转换为抽象语法树(AST)。
- 将抽象语法树转为JavaScript引擎可以执行的二进制代码。
- 执行生成的二进制代码。
在浏览器中,JavaScript引擎和渲染引擎需要互相使用对方的能力。当渲染引擎遇到JavaScript代码时,会将代码交给JavaScript引擎来执行并获取结果;当JavaScript引擎需要访问DOM树时,又需要渲染引擎来提供访问DOM的能力。因此在渲染引擎执行任务的过程中,需要频繁地与JavaScript引擎进行交互。
2. 渲染引擎工作原理
因为渲染引擎是浏览器最核心最关键的模块,因此它也被称为浏览器内核。市面上有多款不同的浏览器,它们之间的主要差别也在于渲染引擎的差别。根据主流浏览器的分类,目前常见的浏览器内核有以下几种:
- Trident(IE)
- Gecko(火狐)
- Blink(Chrome、Opera)
- Webkit(Safari)
Webkit应该是大家最熟悉的,之前它也是Chrome的内核。不过后来由于Chrome内部改造,基于Webkit实现了Blink,但Blink依然保留了Webkit绝大部分的特性。当前国内最常用的浏览器只有Chrome和Safari(像QQ浏览器、360浏览器等一众国产浏览器都是Chrome内核,将其看作Chrome即可),因此了解浏览器的渲染引擎工作原理,我们直接从Webkit入手。
以Webkit为例,其具体的工作步骤如图所示。 从上图中可以看出,渲染引擎的执行过程可以分为 5 个阶段,每个阶段内容如下。
2.1 HTML 解析
该阶段会解析HTML文档。在解析过程如果遇到文档中链接的各种外部资源(如CSS文件、JavaScript文件、图片等),会对这些资源发起请求并加载。解析结束后会生成DOM树。
2.2 CSS 解析
该阶段会识别并加载所有的CSS样式信息,解析后会生成CSSOM树。CSSOM树也是一个树形结构,表示 DOM 树的各个节点对应的样式。
2.3 结构样式合并
该阶段会将前两步生成的DOM树和CSSOM树合并,组成一颗包含HTML结构和CSS样式的渲染树(Render Tree),这颗渲染树包含了页面中所有节点的结构和样式信息。
2.4 布局
ᅟᅠᅟᅠ渲染树已经将页面结构和样式表示完整。如果要显示页面,还必须知道每个元素应该放在浏览器窗口的哪个位置上、占据多大的空间。而这些有关元素的位置、大小等信息,就是该阶段需要完成的页面布局任务。 当页面布局完成,布局信息会被写回渲染树,形成了"布局渲染树"。到这一步为止,所有的计算都还是在内存中执行,用户不可见。接下来我们就可以执行最后一步———绘制页面了。
2.5 绘制
将内存中的布局渲染树绘制成一帧一帧的像素,在浏览器窗口中显示出来。这一步完成后用户才可以看到最终的目标页面。
3. 重排与重绘
在页面初始化完成后,我们还可能会通过CSS、JS来对页面中的元素进行修改,这些修改会重新触发一部分页面渲染的生命周期。重走页面生命周期的这个过程,有两种主要的形式 ——— 重排与重绘。
3.1 重排
当我们修改了某个元素的大小或者位置(比如修改元素的宽高、内外边距,或隐藏元素等)时,浏览器需要重新计算该元素的大小和位置,同时其他元素的大小和位置可能也会受到影响。此时浏览器需要对页面的元素重新排列,这个过程就是重排(也叫回流)。
因为重排会对多个元素产生影响,所以需要重新布局,其生命周期如图所示。 我们以下面这段基础的 HTML 代码片段为例:
<div id="target">
<span id="targetText">我是一个小测试</span>
</div>
<!-- 样式 -->
<style>
#target {
width: 100px;
height: 100px;
}
</style>
当页面初次渲染完成后,在JavaScript中执行以下操作,都会触发页面的重排:
var dom = document.getElementById("target");
// 修改宽高
dom.style.width = "200px";
// 修改边距
dom.style.margin = "10px";
dom.style.padding = "10px";
// 修改元素显示
dom.style.display = "none";
dom.style.display = "flex";
// 添加/删除元素
dom.innerHTML = "<p>子元素</p>";
dom.remove();
// 访问需要即时计算的属性
let top = dom.offsetTop;
let style = dom.getComputedStyle();
需要注意的是,访问元素需要"即时计算"的属性时会发生重排。这是因为这些属性的值需要将元素重排才能计算并得到,而不是为了重新绘制页面。这些属性包括 offsetTop、offsetLeft、scrollTop、scrollLeft、clientTop、clientLeft等。
总结重排的触发场景:当修改元素的几何属性、修改元素结构、或访问需要即时计算的属性时,就会触发页面的重排。
3.2 重绘
当我们修改了元素的显示样式(如字体颜色、背景色、阴影、圆角等)时,元素的几何属性(位置或大小)没有发生变化,也不会导致其他的元素发生变化,此时浏览器便可以跳过页面布局的步骤,直接为该元素绘制新的样式,这个过程叫重绘。
很显然,重绘的性能要比重排好的多。因为它只需要修改目标元素的样式即可,不会产生计算或影响其他元素,从而避免了大面积的元素重排。
var dom = document.getElementById("target");
// 修改以下属性触发重绘
dom.style.color = "blue";
dom.style.background = "red";
dom.style.boxShadow = "1px 1px 2px 2px #555";
当页面发生重绘时,其过程如图所示。 总结重绘的触发场景:当修改元素的显示属性,不涉及任何布局计算时,就会触发页面的重绘。