浏览器的工作原理

638 阅读5分钟

浏览器的工作原理

介绍

当今主流的浏览器有很多,比如 chrome、firefox、safari、opera,edge,其中 chrome 和 edge 使用的是 Blink 内核,而 firefox 使用的是 Gecko 内核,而 safari 使用的是 Webkit 内核。其中 Blink 内核是根据 Webkit 内核演化而来,而如今的谷歌也得益于 webkit 内核的开源。接下来我们以 chrome 浏览器为例来深入的讲解其工作原理。

chrome

chrome 浏览器是一款多进程浏览器,但是在早期的时候,chrome 浏览器是单进程的,这意味着,所有的 tab 标签页将共用一个进程,那么假如其中一个标签页停止运行了,很有可能整个进程都会崩溃,这是很难接受的。另外,所有的 tab 标签页共用一个进程,这对于 js 来说就可以获取到浏览器内部的所有数据,这是很不安全的。

因此,之后,chrome 进行了一系列的操作,将单进程浏览器演变成多进程的浏览器。

chromeium 内核组成

  • 浏览器进程:负责控制 chrome 浏览器除标签页外的用户界面。
  • GPU 进程:负责整个浏览器界面的渲染。
  • 插件进程:用于控制网站使用的所有插件。
  • 网络进程:负责接收和发起网络请求。
  • 渲染进程:用来显示 tab 标签页中的所有内容,浏览器默认默认情况下会为每个标签页创建一个进程。
  • 缓存进程:处理浏览器的缓存。

在网址栏输入网址会发生什么?

当在浏览器的网址栏输入内容,浏览器的 UI 线程会捕捉你输入的内容,如果是一串网址,则 UI 线程会启动一个网络线程来请求 DNS 域名解析服务器。如果是内容,则会使用内置的搜索引擎类查询。

浏览器请求到内容后会发生到什么?

  • 网络线程请求到数据,会通过 SafeBrowsing 来检测站点数据是否安全,如果不完全会弹出警告,但是你也可以强制访问。

  • 当校验通过,网络线程会告知 UI 线程,UI 线程会创建一个渲染器来渲染页面。

  • 渲染器进程的主线程对 HTML、CSS、JS 等资源进行解析。

  • 首先解析 HTML,构成 DOM 树,DOM 就是文档对象模型。

  • HTML 当中通常包括图片/css 文件等资源,这些资源一般从网络当中,缓存当中读取。但是,当解析遇到<script>标签的时候,会停止进行 HTML 的解析,而是去执行 JS,因为浏览器不知道 JS 脚本是否会对 DOM 进行改动,所以,需要等待 JS 脚本执行完毕后,再继续解析 HTML。所以我们一般会吧<script>标签放到合适的位置,挥着给<script>标签添加上defer,async属性来异步加载 JS 了。 HxUkT.png

    解析 CSS

    和解析 HTML 类似,解析 CSS 也是一样,将 css 文件解析成样式树。

    layout 布局

    将 HTML 和 CSS 都解析成功之后,主线程会遍历 DOM 树和计算好的样式树来生成 layout tree。layout tree 上的每一个节点都记录了对应的 x,y 坐标以及边框尺寸。

    注意:DOM tree 和 layout tree 是两个不同的 tree,他们之间并不是一一对应的,比如在 DOM tree 当中display:none的节点不会在 layout tree 当中出现,而在 before 伪类元素中,添加了context属性的值不会出现在 DOM tree 当中,但是会出现在 layout tree 当中。

    绘制页面

    当 layout tree 生成完之后,就来到了绘制页面的过程。首先遍历 layout tree 生成绘制顺序表和 layer tree。然后将 layer tree 与绘制顺序表一起发送给合成器线程,合成器线程将 layer tree 和顺序表分解成图块传给栅格线程。栅格线程对其进行栅格化操作,栅格化完成之后,将栅格化后的draw quads图块信息传递回合成器线程。根据这些信息,合成器线程合成一个合成器帧。然后通过 IPC 管道传递给浏览器进程,之后浏览器进程传送给渲染器进程。 HxWWv.png

    重排

    当我们修改了一个元素的大小、位置、属性的时候,会重新进行样式计算,布局、绘制、以及后面的所有流程。

    重绘

    当我们更改一个元素的颜色属性时,不会重新触发布局,但还是会触发样式计算和绘制,这就是重绘。

    重排、重绘与 JS

    重排和重绘都会占用主线程的资源,而 javascript 也是在主线程当中运行的,这样就会出现主线程的抢占。

    如果有一个不断进行重排与重绘的动画,在一个时间帧当中,当动画执行完,剩下的时间才会执行 JS,但是当时间帧执行完毕,JS 没有执行完,就会抢占主线程,这样动画就无法执行,动画在页面上就会出现卡顿的现象。

    优化方法

    • 使用requestAnimationFrame来进行优化,这个 API 可以将 JS 分解成更小的任务块,分到每一个时间帧当中这样就不会出现 JS 因为执行时间过长而抢占主线程的情况。
    • 在栅格化线程和合成器线程中是不会和 JS 抢占主线程的,而 CSS 当中的transfrom属性实现的动画不会在主线程当中运行,而是在合成器线程和栅格化线程当中运行。更重要的是,经过transfrom来实现的动画不需要进行布局与绘制,样式计算等操作,节省了大量的时间。