浏览器

单进程浏览器

早期时代的浏览器是单进程的,顾名思义。单进程浏览器是指浏览器的所有功能模块都是运行在同一个进程的,包括网络、插件、js 运行环境、渲染引擎和页面等

单进程浏览器的架构如下图:
屏幕截图 2021-11-30 223137
问题显而易见:

  1. 不稳定: 网络、插件、js 运行环境、渲染引擎和页面任何一个意外的崩溃,都会导致整个浏览器崩溃
  2. 不流畅:比如页面线程包含了 js 运行环境,页面渲染,和其他的模块,意味着同一线程同一时刻只能有一个模块运行,如果 js 过大,或者进入无限循环,那么其他模块就没有机会运行,造成卡顿的感觉
    页面的内存泄漏也是单进程变慢的原因,运行一个复杂点的页面再关闭页面。会存在内存不能完全回收的情况,这样导致约积占用内存越高,浏览器变得越慢
  3. 不安全:前面所说,插件的模块也是存在于同一个进程,那么其他模块就有可能收到插件模块的影响,通过插件可以获取系统任意资源,当你在页面运行一个插件时,意味着这个插件能够操作你的电脑,可能释放病毒,窃取密码等

多进程浏览器

现在的浏览器就是多进程浏览器,先看看现在的多进程浏览器架构,以Chrome为例,扔一张图看看:

屏幕截图 2021-11-30 225146

从图中可以看出最新的Chrome浏览器: 1个浏览器主进程1个GPU进程1个网络进程多个渲染进程多个插件进程

  • 浏览器进程:负责界面显示、子进程管理等,同时提供存储功能,localStorage、cookie、session 能力
  • 渲染进程:核心任务将 HTML、CSS 和 JS 转换为网页,排版引擎,V8 引擎都是运行在该进程中,默认情况,一个 tab 创建一个渲染进程,同域共享同一个渲染进程,处于安全考虑,运行在沙箱模式下
  • GPU 进程:用于 UI 界面 GPU 绘制
  • 网络进程:负责网络资源加载
  • 插件进程:负责浏览器插件运行
  • 等等

可以看出,多进程的浏览器提升了浏览器的稳定性流畅性、以及安全性,但不可避免带来一些问题:

  • 更高的资源占用: 这么多的进程,那么意味着浏览器会消耗更多的内存(其实现在电脑配置都这么高了,这点其实不主要)
  • 更复杂的体系架构: 浏览器各模块之间的扩展性差等问题,导致现在架构很难适应新需求

不过有好消息,为了解决以上问题,Chrome团队使用 面向服务的架构(SOA) 思想,就是说Chrome整体架构会朝向现代操作系统所采用的 面向服务的架构(SOA),如图:

20231753

Chrome 最终要把 UI、文件、设备、网络等模块重构为基础服务,类似操作系统底层服务,这一步正在逐步构建。

从输入 url 到页面展示,发生了什么?

用户输入

  • 判断关键词
  • 如果是搜索内容,则地址栏会使用浏览器搜索引擎
  • 如果是符合 URL 规则,则合成完整的 URL
  • 回车之后,浏览器会给当前的页面执行一次 beforeunload,可以用来取消导航

URL 请求过程

  • 进入资源请求过程,浏览器进程通过(IPC)URL 请求发送到网络进程,网络进程发起请求
  • 检查本地是否有缓存,如果有直接返回资源给浏览器进程,如果没有,直接进入网络请求
  • DNS 解析,获取 IP 地址,如果是 HTTPS 还要建立 TLS 连接
  • 利用 IP 地址建立 TCP 连接,浏览器端构建请求行、请求头,相关域 cookie 等数据加到请求头,向服务器发送请求信息
  • 服务器根据请求信息生成响应数据,并发给网络进程,然后网络进程开始解析响应头等信息

(1) 重定向

如果网络进程在解析响应头,发现状态码是 301 或者 302,那么说明需要浏览器重定向到响应头 Location 指定的 URL,这时,请求再次从头开始

(2) 响应数据类型处理

网络进程在解析响应头时,去告诉浏览器,然后根据不同的 Content-Type 类型,来决定如何显示响应体内容。
需要注意的时:如果服务器配置 Conent-Type 不正确,比如将 text/html 类型的配置成 application/octet-stream 类型,那么浏览器会曲解文件内容。一个 html 文件变成了一个下载文件,这时请求就会交给浏览器的下载管理,并不会交给渲染进程,这个时候导航流程就结束了。如果时 html 文件,那么将继续准备渲染进程

准备渲染进程

  • 默认情况,浏览器会为每个页面分配一个渲染进程。有一些情况会复用同一个渲染进程,比如:同域(根域名)的情况。这种策略叫做 process-per-site-instance
  • 准备好之后,等待网络进程请求的资源,准备解析文档

提交文档

所谓提交文档,就是浏览器进程将网络进程接受的 HTML 数据提交给渲染进程,这个过程,浏览器进程网络进程渲染进程时紧密联系的

  • 浏览器进程接收到网络进程的响应头之后,向渲染进程发起提交文档的消息
  • 渲染进程收到提交文档的信息后,和网络进程建立传输数据的管道
  • 渲染进程网络进程数据传输完成之后,渲染进程会返回确认提交的信息给浏览器进程
  • 浏览器进程收到确认提交之后,会更新浏览器的界面状态,比如,安全状态、前进后退状态、tab 的转圈圈状态

渲染阶段

构建 DOM

样式计算

样式由:选择器属性组成

  1. CSS 转换为浏览器理解的结构(CSS 样式来源主要有三种)
  • 通过 link 外部引入
  • <style>标记内的文件
  • 元素的 style 属性内联 CSS
  1. 转换样式表中的属性值,使其标准化
  • 比如:em->pxbold->700bule->rbg(0, 0, 255)

计算出 DOM 树每个节点的具体样式

  1. 计算过程遵循 CSS继承样式层叠两个规则
  • 继承:继承如图 fonst-size 解释:

    屏幕截图 2021-12-06 221922

  • 样式层叠:它定义了如何合并来自多个源的属性值的算法

布局

  1. 创建布局树
    在显示之前,我们还要额外构建一个包含可见元素的布局树,由 DOM 树和 CSS 生成布局树
  • 遍历 DOM 树中可见节点,把节点加到布局树
  • 不可见的会被忽略 。如 head 标签下的全部内容、display:none 的元素
  1. 布局计算
    计算布局树节点的坐标位置,(布局操作时,会把布局计算结果重新写回布局树中)

分层

一些复杂的 3D 变换、页面滚动、或者使用 z-indexing 做 z 轴排序等,还涉及到图层,渲染引擎需要为特定的节点生成专用的图层,并生成一颗对应的图层树(LayerTree),
图层和布局树之间的关系:

屏幕截图%202021-11-30%20221332

不是布局树每个节点都包含一个图层,节点没有对应的图层,则从属父节点的图层。

满足下面任意条件,渲染引擎会为其创建新的图层:

  1. 拥有层叠上下文属性的元素
    • position 值为 absolute(绝对定位)relative(相对定位)且 z-index 值不为 auto 的元素
    • position 值为 fixed(固定定位)sticky(粘滞定位)的元素
  2. 需要剪裁(clip)的地方
    • 出现滚动条,滚动条也会被提升为单独层
    • 元素设置 overflow:auto

图层绘制

屏幕截图%202021-12-07%20000234

屏幕截图%202021-12-07%20000506

在该图中,区域 1 就是 document 的绘制列表,拖动区域 2 中的进度条可以重现列表的绘制过程。

栅格化(raster)操作

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

  • 合成线程将图层划分为图块
  • 合成线程会按照视口附近的图块优先生成位图,所谓栅格化,是指将图块转化为位图
  • 渲染进程维护一个栅格化的线程池,图块栅格化在该线程池执行
  • 栅格化使用 GPU 加速生成(GPU 栅格化或者快速栅格化),生成位图在 GPU 进程执行,生成的位图保存在 GPU 内存
  • 涉及到跨进程操作,渲染进程->GPU进程,以下图参考:
    屏幕截图%202021-12-07%20002407

合成和显示

所有图块都被栅格化,合成线程就会生成一个绘制图块的命令——DrawQuad,提交给浏览器进程,最终将页面绘制到内存中,再将内存显示在屏幕上

总结

  1. 渲染进程将 HTML 内容转换为能够读懂的 DOM结构。
  2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
  3. 创建布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成分层树
  5. 为每个图层生成绘制列表,并将其提交到合成线程。
  6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  7. 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
  8. 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

回流和重绘

  1. 了解浏览器的渲染机制
  • 浏览器采用流式布局模型
  • 浏览器会将 HTML 解析成DOM,把CSS解析成CSSOM,把CSSOMDOM结合成一个render tree
  • 有了render tree之后,就知道了节点样式,然后浏览器会计算节点的位置,然后再绘制再页面上
  1. 回流:当我们render tree中的一些元素的结构或者尺寸等发生改变,浏览器重新渲染部分或者全部文档的过程就做回流,导致回流的操作如下
  • 页面首页渲染
  • 浏览器窗口大小变化
  • 内容变化
  • 添加或者删除节点
  • 激活 CSS 伪类
  • 等等
  1. 重绘:当页面中样式的改变不影响它在文档中的位置,浏览器会将新样式赋予给元素,这个过程叫做重绘,导致重绘的操作如下
  • background…
  • visibility
  • 等等
  1. 性能影响:回流的性能消耗比重绘大
  2. 避免性能影响

CSS:

  • 避免使用table布局
  • 避免设置多层内联样式

JS:

  • 避免大量频繁操作DOM
  • 对于大量插入DOM的操作,建议使用文档片段,也就是documentFragment
  1. 结合图来看:
    • 回流
      屏幕截图%202021-12-07%20003743
    • 重绘
      屏幕截图%202021-12-07%20003927
    • 直接合成(相对回流和重绘大大提升绘制效率)
      屏幕截图%202021-12-07%20004017

浏览器存储

浏览器窗口通讯

发送端

1
otherWindow.postMessage(message, targetOrigin, [transfer]);

接收端

1
2
3
window.addEventListener('message', function (e) {  // 监听 message 事件
console.log("从"+ e.origin +"收到消息: " + e.data);
});
作者

wuxunyu

发布于

2022-06-28

更新于

2022-09-05

许可协议