本文整理自知乎黄子毅的博文精读《深入了解现代浏览器一》
感谢segmentfault童欧巴作者的深入理解JS引擎
感谢b站up主卢克儿的精彩视频浏览器是如何运作的
感谢简书作者浪里行舟博文详解浏览器存储
1. 浏览器运行的基座
计算机硬件提供接口 -> 汇编语言调用接口 -> 操作系统(用C语言)管理硬件 -> 应用程序(浏览器)
| 标题 | 内容 | 备注 |
|---|---|---|
| 硬件 CPU/GPU 等 | 硬件各自提供接口 供汇编语言调用 | |
| 汇编语言 | 调用硬件接口来操作硬件 | |
| 操作系统 | 用C语言管理硬件 进程调度、内存分配 | 基于硬件接口和汇编语言 |
| 应用程序 | 通过操作系统间接操作硬件 | 不直接和硬件打交道 |
浏览器是应用程序
网页浏览器是一种从Web获取和显示页面的应用程序,还可以让用户通过超链接访问更多页面。浏览器运行在操作系统之上。不直接和硬件打交道,而是通过操作系统间接操作硬件。
浏览器统一了操作系统多平台对应的客户端的多样性,实现跨平台并提供统一的编程接口,可以将浏览器看作操作系统之上的操作系统。
CPU
中央处理器,可以处理几乎所有计算。计算能力很强,但只能一件件事处理。
| 不同CPU | 内容 |
|---|---|
| 以前的CPU | 单核 |
| 现在大部分笔记本 | 多核 |
| 专业服务器 | 100多核 |
GPU
一开始是为图像处理设计的(处理像素点),所以拥有大量并行的处理简单事物的能力,非常适合用来做矩阵运算,而矩阵运算又是计算机图形学的基础,所以大量用在可视化领域。
操作系统
操作系统可以帮助我们解决软件治理的问题。但是操作系统有很多(Windows、Mac、Linux、Android、iOS、鸿蒙),使用这些操作系统的设备更多,为了消除客户端的多样性,实现跨平台并提供统一的编程接口,浏览器便诞生了。
为了让程序运行的更安全,操作系统创造了进程与线程的概念。
进程
每个应用程序必须至少启动一个进程来执行其功能,每个程序需要运行许多任务,进程就会创建一些线程来帮助他去执行这些小的任务。这些线程共享内存空间,不需通信就能交流。(内存地址相互隔离的进程间通信通过IPC进行通信)。
进程是操作系统进行资源分配(可以分配独立的内存空间)和调度的基本单元,可申请和拥有计算机资源,进程是程序的基本执行实体。
进程之间相互独立,在一个进程中可以创建一个新进程,并与之通信,所以浏览器就采用了这种策略,将 UI、网络、渲染、插件、存储等模块进程独立,并且任意挂掉后都可以被重新唤起。
线程
线程是操作系统能够进行运算调度的最小单位,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
启动一个程序时,创建一个进程执行任务代码,同时为该进程分配内存空间,该程序的状态都保存在该内存空间里,应用关闭时,内存空间被回收。两个进程传递数据通过IPC传递
几者关系
CPU、GPU 都是计算机硬件,这些硬件各自都提供了一些接口供汇编语言调用;而操作系统则基于它们之上用 C 语言(如 linux)将硬件管理了起来,包括进程调度、内存分配、用户内核态切换等等;运行在操作系统之上的则是应用程序了,所以应用程序不直接和硬件打交道,而是通过操作系统间接操作硬件。
应用程序为何不直接操作硬件
这样做有巨大的安全隐患,因为硬件是没有任何抽象与安全措施的,这意味着理论上,在你打开网页时,JS 可以直接访问你的任意内存地址,读取你的聊天记录,甚至读取历史输入的银行卡密码进行转账操作。
浏览器结构
浏览器分为两部分:Shell(用户界面) 和 内核。其中内核主要有:渲染引擎、JS引擎、其他模块。
| 浏览器内核 | 内容 | 浏览器内核 | 备注 |
|---|---|---|---|
| 展示资源 | 在窗口展示目标网络资源 | 渲染引擎 Blink (新版本的chrome) | 解析资源文件(HTML、CSS、JS)并渲染在屏幕上 |
| 功能交互 | 向服务器发送请求 | JS引擎 Chrome V8引擎 | 运行过程中解析JS源代码并将其转换成可执行的机器码并执行 |
- 2001年,XP系统发布,IE6浏览器
2. 浏览器架构
浏览器功能模块
| 功能模块 | 内容 |
|---|---|
| 浏览器模块 | 负责整个浏览器内行为协调,调用各个模块。 |
| 网络模块 | 负责网络 I/O |
| 存储模块 | 负责本地 I/O |
| 用户界面模块UI | 负责浏览器提供给用户的界面模块 |
| GPU 模块 | 绘图 |
| 渲染模块 | 渲染网页 |
| 设备模块 | 负责与各种本地设备交互 |
| 插件模块 | 负责处理各类浏览器插件 |
| 两种架构设计 | 内容 | 备注 |
|---|---|---|
| 少进程 | 将所有模块放在一个或几个进程里 每个模块一个进程 | 最大程度共享了内存空间 稳定性差 |
| 多进程 | 为每个模块开辟一个进程 | 占用内存大 |
服务化 - 单/多进程弹性架构
Chrome 并不满足于采用一种架构,而是在不同环境下切换不同的架构。Chrome 将各功能模块化后,就可以自由决定当前将哪些模块放在一个进程中,将哪些模块启动独立进程,即可以在运行时决定采用哪套进程架构。
这样做的好处是,可以在资源受限的机器上开启单进程模式,以尽量节约内存开销,实际上在手机应用上就是这么做的;而在资源丰富、内核数量充足的机器上采用独立进程模式,虽然消耗了更多资源,但获得了更好的稳定性。
浏览器的主从架构
浏览器模块为主模块,协调和维持其他各模块的正常工作,其他模块失去响应时等待或重新唤起,或在模块销毁时进行内存回收。
各从模块分工明确
3. 浏览器渲染网页
渲染网页就是解析接收到的HTML、CSS和JS等文件。
| 标题 | 类比 |
|---|---|
| HTML | 施工合同 |
| DOM | 网页的迷你框架结构 |
| CSS | 装饰工程 |
| HTML | 网页的框架结构 |
1. 构建DOM
浏览器发送请求,服务器响应给浏览器HTML文件,UI线程创建一个渲染器进程来渲染页面,浏览器进程通过IPC管道将数据传递给渲染器进程,此时为显示字节内容的;
字节内容的HTML转换为字符内容的HTML(程序员看懂的HTML代码);
字符内容的HTML转换为Token符号标签,即机器看得懂的HTML;
Token符号标签转换为节点对象,最后再连在一起,形成DOM(浏览器自己的语言),每个节点对象相连,形成父子关系,
2. 构建CSSOM
浏览器在构建DOM时,遇到link标签,向服务器发送请求得到CSS文件。构建CSSOM不可以部分解析,因为层叠样式表需要全部都跑完,才是最后的样式。
字节-字符-Token-节点-CSS对象模型(CSSOM)
3. 构建渲染树
DOM和CSSOM的合成即为渲染树。其任务是匹配DOM和CSSOM的节点,并捕获可见内容。
4. 布局
获取渲染树的结构、节点位置和大小。布局依据盒子模型来进行,即每个元素都用一个盒子来表示,然后这些盒子在页面上进行排列和嵌套
5. 绘制
把渲染树以像素的形式绘制在页面
4. 浏览器发展简史
| 年份 | 浏览器 | 备注 |
|---|---|---|
| 1991 | WorldWideWeb | 只支持显示文本、图片 |
| 1993 | Mosaic | 可同时显示文本和图像 |
| 1994 | 网景 | 只显示静态HTML,没有JS、CSS |
| 1995 | IE 1.0/2.0 | |
| 1996 | IE 3.0 | 与windows绑定 |
| 1998 | 火狐FireFox | |
| 2003 | 苹果Safari | OS系统内置 2005年开源浏览器内核 |
| 2004 | FireFox 1.0 | |
| 2008 | Chrome 1.0 |
5.浏览器存储
cookie
cookie指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。 cookie是服务端生成,客户端进行维护和存储,存储在内存或者磁盘中。通过cookie,可以让服务器知道请求是来源哪个客户端,就可以进行客户端状态的维护。
cookie 主要用于:会话状态管理(如用户登录状态、购物车、游戏分数)、个性化设置(如用户自定义设置、主题等)、浏览器行为跟踪(如跟踪分析用户行为等)。
cookie的来源
因为HTTP协议是无状态的,其自身不对请求和响应之间的通信状态进行保存,服务器不知道用户上一次做了什么,阻碍交互式Web应用程序的实现。cookie就是用来绕开HTTP的无状态性的“额外手段”之一。服务器可以设置或读取cookie中包含信息,借此维护用户跟服务器会话中的状态。
在购物场景中,当用户选购了第一项商品,服务器在向用户发送网页的同时,还发送了一段cookie,记录着那项商品的信息。当用户访问另一个页面,浏览器会把cookie发送给服务器,于是服务器知道他之前选购了什么。用户继续选购饮料,服务器就在原来那段Cookie里追加新的商品信息。结账时,服务器读取发送来的cookie就行了。
cookie的原理及其构成
第一次访问网站的时候,浏览器发出请求,服务器端生成 cookie在响应中通过Set-Cookie头部告知客户端,客户端得到 cookie后,后续请求都会自动将 cookie头部携带至请求中发送给服务器。
// 一个HTTP响应:
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value
Other-header: other-header-value
JS 中的cookie
cookie的生成方式有两种,一种是在响应中通过Set-Cookie头部告知客户端;另外一种是在JS中通过document.cookie可以读写cookie。
cookie的缺陷
每个cookie的大小为4KB,只能用来存取少量的信息,浏览器对一个站点的cookie个数也是有限制的,不超300个。
Web Storage
Web Storage的目的是解决通过客户端存储不需要频繁发送回服务器的数据时使用cookie的问题。Web Storage API包含了两个对象:localStorage和sessionStorage,本质上是映射字符串键和值的对象化。
sessionStorage对象只存储会话数据,存储到浏览器关闭。数据不受页面刷新影响,可以在浏览器崩溃并重启后恢复。
sessionStorage.setItem('user_name', 'juejin') // 存储数据
sessionStorage.getItem('user_name') // 读取数据
sessionStorage.removeItem('user_name') // 删除数据
sessionStorage.clear()
存储在 localStorage 的数据可以长期保留;而当页面会话结束(即当页面被关闭时),存储在 sessionStorage 的数据会被清除 。电商网站会用它来存储 Base64 格式的图片字符串,还会用它存储一些不经常更新的 CSS、JS 等静态资源。
cookie与Web Storage的区别
共同点都是保存在浏览器端,且都遵循同源策略。
不同点在于生命周期与作用域的不同。
对于作用域:localStorage只要同源就能读取/修改到同一份localStorage数据。sessionStorage在同源基础上,还要求在同一Tab上。
对于生命周期:localStorage 是持久化的本地存储,手动删除数据消失;而 sessionStorage 是临时性的本地存储,它是会话级别的存储,当会话结束(页面被关闭)时,存储内容也随之被释放。