浏览器原理学习笔记01—宏观视角下的浏览器
Write By CS逍遥剑仙
我的主页: csxiaoyao.com
GitHub: github.com/csxiaoyaojianxian
Email: sunjianfeng@csxiaoyao.com
QQ: 1724338257
1. 起步
前端知识的深入绕不开浏览器原理的学习,本系列文章为笔者学习浏览器原理后整理的笔记,学习的过程以极客时间《浏览器工作原理与实践》为主线,对原文的重要知识点进行了归纳整理,针对其中的部分细节知识点查阅了大量资料进行扩展并梳理总结,其中有部分demo和图片参考自原文。
2. Chrome架构演进
浏览器三大进化路线:应用程序 Web 化,Web 应用移动化,Web 操作系统化。早在2007年之前,市面上浏览器都是单进程的,即浏览器所有的功能模块运行在同一个进程中,因此不稳定、不流畅、不安全。
2.1 2008年发布时的进程架构
2008年发布的 Chrome 拆分为 3 种进程,除主进程外,页面运行在单独的渲染进程中,同时页面里的插件运行在单独的插件进程中,而进程间通过 IPC 机制进行通信(图中虚线部分)。
2.2 目前多进程架构
最新的 Chrome 浏览器包括 5 类进程:
- 浏览器进程 x1:负责界面显示、用户交互、子进程管理、存储等功能
- 渲染进程 xn:将 HTML、CSS 和 JavaScript 转换为可交互的网页,运行着排版引擎 Blink 和 JavaScript 引擎 V8。Chrome 默认为每个 Tab 标签创建一个渲染进程并运行在沙箱模式下
- GPU 进程 x1:浏览器使用 GPU 初衷是为了实现 3D CSS 效果,随后网页、Chrome 的 UI 界面都普遍采用 GPU 来绘制,因此独立出 GPU 进程
- 网络进程 x1:独立出来负责页面网络资源加载
- 插件进程 xn:插件易崩溃,因此需要通过插件进程来隔离插件的运行
打开 1 个页面,至少有 4 个进程:浏览器进程、网络进程、GPU 进程、渲染进程,可能有插件进程。
2.3 未来面向服务(SOA)的架构
为了解决 资源占用 和 体系架构复杂 的问题,Chrome 官方团队2016 年使用 SOA
思想设计了新的 Chrome 架构,原来的模块会被重构成独立的服务,每个服务在独立的进程中运行,使用定义好的接口访问服务,通过 IPC 通信。
同时在性能受限的设备上 Chrome 还提供灵活的弹性架构,会将很多服务整合到一个进程中,从而节省内存占用。
3. TCP协议
图为简化的 TCP 网络四层传输模型,UDP与之相似,不同之处在于 TCP 头的信息保证了大数据块传输的完整性、可靠性,但牺牲了数据包的传输速度,三次握手 和 数据包排序校验机制 等过程把传输过程中数据包的数量提高了一倍。
4. HTTP请求流程
HTTP 协议是建立在 TCP 连接基础之上的一种允许浏览器向服务器获取资源的协议。
4.1 浏览器端发起 HTTP 请求流程
-
构建请求
构建请求行信息,如
GET /index.html HTTP1.1
-
查找缓存
-
准备 IP 地址和端口
使用
DNS
或DNS缓存
获取对应IP -
等待 TCP 队列
Chrome TCP 队列机制:同一域名最多只能同时建立 6 个 TCP 连接,超出的请求会排队等待
-
建立 TCP 连接
-
发送 HTTP 请求
4.2 服务器端处理 HTTP 请求流程
4.3 浏览器资源缓存(cache)处理
浏览器会在服务器返回时根据响应头中的 Cache-Control
字段的过期时长来设置资源缓存,下次请求直接读取未过期缓存。若缓存过期,浏览器则会发起网络请求,并在 HTTP 请求头 中带上资源 key
,如:If-None-Match:"4f80f-13c-3a1xb12a"
,服务器根据资源 key 值判断请求的资源是否有更新,若没有更新则仅返回 304 状态码,有更新则直接返回最新资源。
4.4 登录状态保持(cookie)
登录成功后会生成标识用户身份的字符串并写到响应头的 Set-Cookie
字段里,浏览器解析存到本地,下次请求自动在请求头的 Cookie
字段中添加该值。
5. 导航流程:从输入URL到页面展示
-
用户输入
-
URL 请求
-
准备渲染进程
Chrome 默认会为每个打开的页面分配一个渲染进程,但如果新页面和当前页面属于同一站点(根域名+协议相同)则会复用父页面的渲染进程(process-per-site-instance)。
-
提交文档
此处 文档 是指 URL 请求的响应体数据,浏览器进程发出 提交文档消息,渲染进程接收到后会和网络
csxiaoyao.com 进程建立传输数据的 管道,文档数据传输完成后渲染进程返回 确认提交消息 给浏览器进程,浏览器进程更新浏览器界面状态,并更新 Web 页面。 -
渲染阶段
6. 渲染流程
下面将依次从 DOM、Style、Layout、Layer、Paint、Tiles & Raster、DrawQuad 分析渲染流程,详见《浏览器中的页面渲染》。
6.1 DOM: 构建 DOM 树
document 即 DOM 结构,区别于 HTML 的是,DOM 是保存在内存中的树状结构,可通过 JavaScript 来查询或修改其内容。
6.2 Style: 样式计算
6.2.1 格式转换
当渲染引擎接收到 CSS 文本时(link外部文件、style标签内、内联样式),会执行转换操作将 CSS 文本转换为浏览器可以理解的结构(styleSheets),在浏览器的 console 中可输入命令 document.styleSheets
查看。
6.2.2 标准化属性值
6.2.3 计算 DOM 树中每个节点的具体样式
根据 CSS 的 继承 和 层叠 规则计算每个 DOM 节点的样式并被保存在 ComputedStyle
结构内,可在开发者工具内查看。
6.3 Layout: 布局阶段
除了 DOM 树和 DOM 树中元素的样式,显示页面还需要通过创建 布局树 和布局计算来得到 DOM 元素的几何位置信息。
6.3.1 创建布局树
在显示之前还要额外地构建一棵只包含可见元素布局树,遍历 DOM 树中的所有可见节点加到布局中。
6.3.2 布局计算
计算布局树节点的坐标位置的计算过程非常复杂,此处略过,执行布局操作时会把布局运算的结果重新写回布局树中,所以布局树既是输入内容也是输出内容,不合理,Chrome 下一代布局系统 LayoutNG 将解决这个问题。
6.4 Layer: 分层
创建布局后,渲染引擎还要为特定的节点生成专用的图层,并生成对应的图层树(LayerTree),在后面章节会介绍。
布局树的节点默认从属于父节点图层,满足下面两点中一点的元素可被提升为单独的一个图层:
6.5 Paint: 图层绘制,生成绘制列表
渲染引擎会对图层树中的每个图层进行绘制,首先会生成绘制列表,可以在开发者工具的 Layers 标签中选择 document 层查看实际绘制列表。
Tips:
图层绘制并非真正绘出帧图片,而是生成绘制指令列表,绘制过程即完成
6.6 Tiles & Raster: 图块划分 & 栅格化操作
绘制列表只是用来记录绘制顺序和指令,主线程会把该绘制列表 提交{commit) 给渲染引擎中的 合成线程 进行实际绘制操作。有的图层很大很长,但用户通过 视口(viewport)只能看到页面的很小一部分,为了减小开销,合成线程会将图层划分为 图块(tile),通常大小为 256x256 或 512x512,合成线程会优先把视口附近的图块
6.7 DrawQuad: 合成和显示
当所有图块都被光栅化,合成线程会生成绘制图块命令 DrawQuad 并提交给浏览器进程。浏览器进程里的 viz
组件根据 DrawQuad 消息绘制到内存中,最后显示在显示器上。
7. 重排 / 重绘 / 合成
使用 CSS 的 transform 实现动画效果可以避开重排和重绘,直接在非主线程上执行合成动画操作,并不会占用主线程的资源,效率较高,在页面章节会深入讲解。
减少重排重绘的方法:
- 使用 class 操作样式,而不是频繁操作 style
- 避免使用 table 布局
- 批量处理 dom 操作,例如 createDocumentFragment,或使用框架的虚拟DOM
- 对 resize 等事件防抖处理
- dom 属性读写分离
- 使用 will-change: transform 做优化