浏览器工作原理和实践(一)——浏览器

  《浏览器工作原理与实践》是极客时间上的一个浏览器学习系列,在学习之后特在此做记录和总结。

一、Chrome架构

1)线程和进程

  Chrome打开一个页面会启动4个进程:网络进程、GPU进程、浏览器主进程和渲染进程。

  一个进程就是一个程序的运行实例,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,这样一个运行环境叫进程。

  线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率。

  进程和线程之间的关系有以下 4 个特点:

  (1)进程中的任意一线程执行出错,都会导致整个进程的崩溃。

  (2)线程之间共享进程中的数据。

  (3)当一个进程关闭之后,操作系统会回收进程所占用的内存。

  (4)进程之间的内容相互隔离。

  2)Chrome进程架构

  最新的 Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程,如下图所示。

  浏览器工作原理和实践(一)——浏览器_第1张图片

  (1)浏览器主进程:负责界面显示、用户交互、子进程管理,同时提供存储等功能。

  (2)渲染进程:核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。

  (3)GPU进程:GPU 的使用初衷是为了实现 3D CSS 的效果,随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。

  (4)网络进程:负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的。

  (5)插件进程:负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。

二、网络协议

1)TCP协议

  在衡量 Web 页面性能时有一个重要的指标FP(First Paint),它是指从页面加载到首次开始绘制的时长。而影响 FP 指标的一个重要因素就是网络加载速度。

  互联网,实际上是一套理念和协议组成的体系架构。互联网中的数据是通过数据包来传输的。

  (1)IP:把数据包送达目的主机。

  (2)UDP:把数据包送达应用程序。

  (3)TCP:把数据完整地送达应用程序。

2)HTTP协议

  HTTP 是一种允许浏览器向服务器获取资源的协议,是 Web 的基础。

  浏览器端发起 HTTP 请求流程:

  (1)构建请求,构建好后,浏览器准备发起网络请求。

  (2)查找缓存,当浏览器发现请求的资源已经在浏览器缓存中存有副本,它会拦截请求,返回该资源的副本,并直接结束请求。

  (3)准备 IP 地址和端口,HTTP 的内容是通过 TCP 来传输的,第一步就是先建立TCP连接,而请求 DNS(DNS数据缓存服务) 可返回域名对应的 IP。

  (4)等待 TCP 队列,同一个域名同时最多只能建立 6 个 TCP 连接,如果同时有 10 个请求发生,那么其中 4 个会进入等待状态。

  (5)建立 TCP 连接,快乐地和服务器握手。

  (6)发送 HTTP 请求,和服务器进行通信,向服务器发送请求行,包括请求方法、请求 URI(Uniform Resource Identifier)和 HTTP 版本协议。

  服务器端处理 HTTP 请求流程:

  (1)返回请求,一旦服务器处理结束,便可以返回响应行,包括协议版本和状态码。

  (2)断开连接,但浏览器或者服务器在其头信息中加入了:Connection:Keep-Alive,那么 TCP 连接在发送后将仍然保持打开状态。

  (3)重定向,当两个 URL 不一样时,会涉及一个重定向操作。

  为什么很多站点第二次打开速度会很快?因为 DNS 缓存和页面资源缓存这两块数据是会被浏览器缓存的。

  登录状态是如何保持的?浏览器页面状态是通过使用 Cookie 来实现的。如果服务器端发送的响应头内有 Set-Cookie 的字段,那么浏览器就会将该字段的内容保存到本地。

  下面是张详细的“HTTP 请求示意图”,用来展现浏览器中的 HTTP 请求所经历的各个阶段。

  浏览器工作原理和实践(一)——浏览器_第2张图片

  浏览器中的 HTTP 请求从发起到结束一共经历了八个阶段:构建请求、查找缓存、准备 IP 和端口、等待 TCP 队列、建立 TCP 连接、发起 HTTP 请求、服务器处理请求、服务器返回请求和断开连接。

三、导航流程

  在浏览器里,从输入 URL 到页面展示,这中间发生了什么?其中涉及到了网络、操作系统、Web 等一系列的知识,如下图所示。

  浏览器工作原理和实践(一)——浏览器_第3张图片

  整个流程大致描述如下:

  (1)首先,浏览器进程接收到用户输入的 URL 请求,浏览器进程便将该 URL 转发给网络进程。

  (2)然后,在网络进程中发起真正的 URL 请求。

  (3)接着,网络进程接收到了响应头数据,便解析响应头数据,并将数据转发给浏览器进程。

  (4)浏览器进程接收到网络进程的响应头数据之后,发送“提交导航 (Commit Navigation)”消息到渲染进程;

  (5)渲染进程接收到“提交导航”的消息之后,便开始准备接收 HTML 数据,接收数据的方式是直接和网络进程建立数据管道;

  (6)最后,渲染进程会向浏览器进程“确认提交”,这是告诉浏览器进程:“已经准备好接受和解析页面数据了”。

  (7)浏览器进程接收到渲染进程“提交文档”的消息之后,便开始移除之前旧的文档,然后更新浏览器进程中的页面状态。

  这其中,用户发出 URL 请求到页面开始解析的这个过程,就叫做导航。

1)用户输入

  当用户在地址栏中输入一个查询关键字时,地址栏会判断输入的关键字是搜索内容,还是请求的 URL。

  当用户输入关键字并键入回车之后,这意味着当前页面即将要被替换成新的页面,不过在这个流程继续之前,浏览器还给了当前页面一次执行 beforeunload 事件的机会。

  beforeunload 事件允许页面在退出之前执行一些数据清理操作,还可以询问用户是否要离开当前页面,比如当前页面可能有未提交完成的表单等情况,因此用户可以通过 beforeunload 事件来取消导航,让浏览器不再执行任何后续工作。

2)URL 请求过程

  浏览器进程会通过进程间通信(IPC)把 URL 请求发送至网络进程,网络进程接收到 URL 请求后,会在这里发起真正的 URL 请求流程(参考上面的HTTP请求流程)。

3)准备渲染进程

  Chrome 会为每个页面分配一个渲染进程,也就是说,每打开一个新页面就会配套创建一个新的渲染进程。

  但如果属于同一站点,即根域名(例如geekbang.org)和协议(例如https:// 或者 http://)都相同,那么新页面会复用父页面的渲染进程。官方把这个默认策略叫 process-per-site-instance。

4)提交文档

  提交文档,就是指浏览器进程将网络进程接收到的 HTML 数据提交给渲染进程,具体流程是这样的:

  (1)首先当浏览器进程接收到网络进程的响应头数据之后,便向渲染进程发起“提交文档”的消息;

  (2)渲染进程接收到“提交文档”的消息后,会和网络进程建立传输数据的“管道”;

  (3)等文档数据传输完成之后,渲染进程会返回“确认提交”的消息给浏览器进程;

  (4)浏览器进程在收到“确认提交”的消息后,会更新浏览器界面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 Web 页面。

5)渲染阶段

  一旦文档被提交,渲染进程便开始页面解析和子资源加载。

  一旦页面生成完成,渲染进程会发送一个消息给浏览器进程,浏览器接收到消息后,会停止标签图标上的加载动画。

四、渲染流程

  HTML 的内容是由标记和文本组成。标记也称为标签,每个标签都有它自己的语义,浏览器会根据标签的语义来正确展示 HTML 内容。

  如果需要改变 HTML 的字体颜色、大小等信息,就需要用到 CSS。CSS 又称为层叠样式表,是由选择器和属性组成。

  至于 JavaScript(简称为 JS),使用它可以使网页的内容“动”起来。

  由于渲染机制过于复杂,所以渲染模块在执行过程中会被划分为很多子阶段,输入的 HTML 经过这些子阶段,最后输出像素,这样的一个处理流程叫做渲染流水线。

  按照渲染的时间顺序,流水线可分为如下几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。

  浏览器工作原理和实践(一)——浏览器_第4张图片

1)构建 DOM 树

  浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树,如下所示。

  浏览器工作原理和实践(一)——浏览器_第5张图片

2)样式计算

  样式计算(Recalculate Style)地目的是为了计算出 DOM 节点中每个元素的具体样式,这个阶段大体可分为三步来完成。

  (1)把 CSS 转换为浏览器能够理解的结构——styleSheets。

  浏览器工作原理和实践(一)——浏览器_第6张图片

  (2)转换样式表中的属性值,使其标准化,如 2em、blue、bold,需要将它们转换为渲染引擎容易理解的、标准化的计算值。

  浏览器工作原理和实践(一)——浏览器_第7张图片

  (3)计算出 DOM 树中每个节点的具体样式,涉及到 CSS 的继承规则和层叠规则。此阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。

3)布局阶段

  布局就是计算 DOM 树中可见元素几何位置的过程。Chrome 在布局阶段需要完成两个任务:创建布局树和布局计算。

  (1)创建只包含可见元素的布局树。

  (2)计算布局树节点的坐标位置。

  浏览器工作原理和实践(一)——浏览器_第8张图片

4)分层

  渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。

  打开 Chrome 的“开发者工具”,选择“Layers”标签,就可以可视化页面的分层情况,如下图所示。

  浏览器工作原理和实践(一)——浏览器_第9张图片

  下面再来看看这些图层和布局树节点之间的关系。通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。

  浏览器工作原理和实践(一)——浏览器_第10张图片

  满足下面两点中任意一点的元素就可以被提升为单独的一个图层。

  (1)拥有层叠上下文属性的元素,例如明确定位属性的元素、定义透明属性的元素、使用 CSS 滤镜的元素等。

  (2)需要剪裁(clip)的地方也会被创建为图层,如果出现滚动条,滚动条也会被提升为单独的层。

5)图层绘制

  渲染引擎实现图层的绘制与之类似,会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表,如下图所示:

  浏览器工作原理和实践(一)——浏览器_第11张图片

  绘制列表中的指令其实非常简单,比如绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。

6)栅格化操作

  绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。

  当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程。

  合成线程会将图层划分为图块(tile),这些图块的大小通常是 256x256 或者 512x512,如下图所示:

  浏览器工作原理和实践(一)——浏览器_第12张图片

  然后合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。

  所谓栅格化(raster),是指将图块转换为位图。而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的。

  通常,栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。

  浏览器工作原理和实践(一)——浏览器_第13张图片

  从图中可以看出,渲染进程把生成图块的指令发送给 GPU,然后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中。

7)合成和显示

  一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。

  将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

  一个完整的渲染流程大致可总结为如下:

  (1)渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。

  (2)渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。

  (3)创建布局树,并计算元素的布局信息。

  (4)对布局树进行分层,并生成分层树。

  (5)为每个图层生成绘制列表,并将其提交到合成线程。

  (6)合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。

  (7)合成线程发送绘制图块命令 DrawQuad 给浏览器进程。

  (8)浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

  渲染引擎会通过合成线程直接去处理变换,这些变换并没有涉及到主线程,这样就大大提升了渲染的效率。这也是 CSS 动画比 JavaScript 动画高效的原因。

8)重排

  更新了元素的几何属性。

  浏览器工作原理和实践(一)——浏览器_第14张图片

  从上图可以看出,如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。

  无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。

9)重绘

  更新元素的绘制属性。

  浏览器工作原理和实践(一)——浏览器_第15张图片

  从图中可以看出,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。

  相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

10)直接合成

  染引擎将跳过布局和绘制,只执行后续的合成操作,把这个过程叫做合成。

  浏览器工作原理和实践(一)——浏览器_第16张图片

  在上图中,我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。

  这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。

你可能感兴趣的:(浏览器工作原理和实践(一)——浏览器)