浏览器组成部分
Web 浏览器简称为浏览器,是一种用于访问互联网上信息的应用软件。浏览器的主要功能是从服务器检索 Web 资源并将其显示在 Web 浏览器窗口中。
Web 资源通常是 HTML 文档,但也可能是 PDF、图像、音频、视频或其他类型的内容。资源的位置是通过使用 URI(统一资源标识符)指定的。
浏览器包含结构良好的组件,这些组件执行一系列任务让浏览器窗口能显示 Web 资源。
一个 Web 浏览器中,主要组件有:
- 用户界面(User Interface)
- 浏览器引擎(Browser Engine)
- 渲染引擎(Rendering Engine)
- 网络(Networking)
- JS 解释器(JavaScript Interpreter)
- 用户界面后端(UI Backend)
- 数据存储(Data Storage/Persistence)
用户界面
用户界面用于呈现浏览器窗口部件,比如地址栏、前进后退按钮、书签、顶部菜单等。
除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
浏览器引擎
在用户界面和渲染引擎之间传送指令,在客户端本地缓存中读写数据等,是浏览器中各个部分之间相互通信的核心。
渲染引擎
负责在浏览器窗口上显示请求的内容。例如,用户请求一个 HTML 页面,则它负责解析 HTML 文档和 CSS、JS 等,并将解析和格式化的内容显示在屏幕上。
我们平时说的浏览器内核就是指渲染引擎。
现代网络浏览器的渲染引擎:
- Firefox:Gecko Software
- Safari:WebKit
- Chrome、Opera (15 版本之后):Blink
- Internet Explorer:Trident
网络
该模块处理浏览器内的各种网络通信。它使用一组通信协议,如 HTTP、HTTPS、FTP,同时通过 URL 获取请求的资源。
JS 解释器
JavaScript 是一种脚本语言,允许我们动态更新 Web 内容、控制由浏览器的 JS 引擎执行的多媒体和动画图像。
DOM 和 CSSOM 为 JS 提供了一个接口,可以改变页面的 DOM 和 CSSOM。由于浏览器不确定特定的 JS 会做什么,它会在遇到 script
标签后立即暂停 DOM 树的构建。
每个脚本都是一个解析拦截器,会让 DOM 树的构建停止。
JS 引擎在从服务器获取并输入 JS 解析器后立即开始解析代码。它将它们转换为机器理解的代表性对象。在抽象句法结构的树表示中存储所有解析器信息的对象称为对象语法树(AST)。这些对象被送入一个解释器,该解释器将这些对象翻译成字节码。
这些是即时 (JIT) 编译器,这意味着从服务器下载的 JavaScript 文件在客户端的计算机上实时编译。解释器和编译器是结合在一起的。解释器几乎立即执行源代码;编译器生成客户端系统直接执行的机器代码。
不同的浏览器使用不同的 JS 引擎:
- Chrome:V8(Node.js 建立在此之上)
- Mozilla: SpiderMonkey(旧称“松鼠鱼”)
- Microsoft Edge:Chakra(现在已经使用 Chromium 内核的 V8 了)
- Safari:JavaScriptCore / Nitro WebKit
用户界面后端
用来绘制基本的浏览器窗口内控件,如输入框、按钮、选择框、对话框等,根据浏览器不同绘制的视觉效果也不同,但功能都是一样的。具有不特定于某个平台的通用接口。
向上提供公开的接口,向下调用操作系统的用户界面。
数据存储
这是一个持久层。浏览器可能需要在本地(硬盘、内存)保存各种数据,例如 cookie、Web Storage、IndexedDB、WebSQL 和 FileSystem 等存储机制。可通过浏览器引擎提供的 API 进行调用。
浏览器多进程架构
- 进程是 CPU 资源分配的最小单位
- 线程是 CPU 调度的最小单位
进程:对于操作系统来说,一个任务就是一个进程,比如打开一个浏览器就是启动了一个浏览器进程,打开一个 Word 就启动了一个 Word 进程。
线程:在一个进程内部,要同时做多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程。
浏览器会开多个进程:
- 主进程(Browser Process)
- 第三方插件进程(Plugin Process)
- GPU 进程(GPU Process)
- 渲染进程(Renderer Process)
- GUI(图形用户界面)渲染线程
- JS 引擎线程
- 事件触发线程
- 定时触发器线程
- 异步 HTTP 请求线程
主进程
负责浏览器界面的显示与交互。各个页面的管理、创建和销毁其他进程。网络的资源管理、下载等。
第三方插件进程
每种类型的插件对应一个进程,仅当使用该插件时才创建。
GPU 进程
和 GPU 通信,GPU 加速
渲染进程
浏览器的渲染进程是多线程的,主要负责页面渲染、脚本执行、事件处理等。
默认情况下,会为每个 Tab 标签创建⼀个渲染进程:
iframe
:单独进程- 同一站点(协议和根域名相同):公用渲染进程
GUI(图形用户界面)渲染线程
即:主渲染线程
负责渲染浏览器界面,解析 HTML、CSS,构建 DOM 树和 CSSOM 树,布局和绘制等。
当界面初次渲染或需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。
注意,GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起(相当于被冻结了),GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行
JS 引擎线程
JavaScript 引擎负责处理 JavaScript 脚本程序:解析 JavaScript 脚本,运行代码
JS 引擎一直等待着任务队列中任务的到来,然后加以处理,一个 Tab 页(renderer 进程)中无论什么时候都只有一个 JS 线程在运行 JS 程序。
注意,GUI 渲染线程与 JS 引擎线程是互斥的,所以如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
事件触发线程
归属于浏览器而不是 JS 引擎,用来控制事件循环(可以理解为 JS 引擎自己都忙不过来,需要浏览器另开线程协助)
当 JS 引擎执行代码块如 setTimeOut
时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX 异步请求等),会将对应任务添加到事件线程中
当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待 JS 引擎的处理
注意,由于 JS 的单线程关系,所以这些待处理队列中的事件都得排队等待 JS 引擎处理(当 JS 引擎空闲时才会去执行)
定时触发器线程
setInterval
与 setTimeout
所在线程
浏览器定时计数器并不是由 JavaScript 引擎计数的,(因为 JavaScript 引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待 JS 引擎空闲后执行)
注意,W3C 在 HTML 标准中规定要求 setTimeout
中低于 4ms 的时间间隔算为 4ms。
异步 HTTP 请求线程
在 XMLHttpRequest
连接后是通过浏览器新开一个线程请求。
将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,再由 JavaScript 引擎执行。