本文是极客时间上《浏览器工作原理与实践》课程的学习笔记。
进程和线程
一个进程就是一个程序的运行实例。
线程是依附于进程的,而进程中可以使用多线程并行处理提升运算效率。
进程特点
- 进程任意线程出错,会导致整个进程的崩溃!
- 线程之间共享进程中的数据。
- 当进程关闭后,操作系统会回收进程所占用的内存。
- 进程之间相互隔离。如果想做进程间的通信,需要使用进程间通信机制。
浏览器的单进程架构
早期的浏览器是单进程的,一旦出错整个浏览器都挂了。
- 不稳定
- 不流畅
- 不安全
浏览器的多进程架构
- 渲染进程 - 沙箱隔离 - 多个(每个页面一个,统一站点会公用一个渲染进程)
- 插件进程 - 沙箱隔离 - 多个(每个插件一个)
- 网络进程 - 1
- 浏览器主进程 - 1
- GPU进程 - 1
多进程架构的缺点
- 占用资源更高
- 架构更复杂
未来面向服务的架构
原来的各种模块会被重构成独立的服务,每个服务都可以在独立的进程中运行,访问服务必须使用定义好的接口,从而构建一个更内聚、松耦合、易于维护和扩展的系统。
网络加载速度是非常影响首屏渲染速度的。
数据包通信过程
互联网实际上是一套理念和协议组成的体系架构。其中协议是一套众所周知的规则和标准,如果各方统一使用,那么他们之间的通信将变得毫无障碍。
- 先为数据包加上 IP 头,IP 头存储了发送和接收主机的计算机地址。
- IP 协议负责把数据包传送到对方电脑,而 UDP 可以通过端口号将数据包传递给应用程序。
端口号信息会被放到 UDP 头里面,进行传输。
UDP 协议对于发生错误的包不提供重发机制,只是丢弃当钱包。
UDP 不能保证数据的可靠性,但是传输速度非常快。
- 如果是需要传输可靠性的应用,就要引入 TCP 协议了。
TCP 提供了数据包的排序机制,避免丢包。
TCP 提供了丢失数据包的重传机制。
TCP 传输的完整过程为
- 建立连接 —— 三次握手
- 传输数据 —— 保证数据包的有序性和完整性,提供重发、【爱旭机制。
- 断开连接 —— 四次挥手
TCP 和 HTTP 的关系:TCP 负责数据包的完整传输,HTTP 负责报文信息的构建和解析。他们都属于 TCP/IP 协议簇。
HTTP 是一种允许浏览器向服务器获取资源的协议,是 web 的基础。
浏览器发起 HTTP 请求的流程
- 构建请求 —— 请求行、请求头
- 查找缓存 —— 如果有有效缓存直接使用缓存。
3, 准备 IP 地址和端口 —— 建立 TCP 连接,传输 HTTP 协议的数据。
浏览器会请求 DNS 返回域名对应的 IP,当然也可以是 DNS 数据缓存服务。
获取端口号,如果没有指明端口号 HTTP 协议默认是 80 端口。
- 等待 TCP 队列 —— chrome 一个域名同时最多只能建立 6 个 TCP 连接。
- 建立 TCP 连接 —— 三次握手
- 发送 HTTP 请求 —— HTTP 请求报文有请求行、请求头、请求体。
服务器处理 HTTP 请求流程
- 返回请求 —— 请求处理结束,返回数据给浏览器。
可以使用 curl 来查看返回请求数据
返回数据符合 HTTP 响应数据的格式。
- 断开连接 —— 浏览器拿到数据,通常情况下就可以挥手断开连接了。
可以加上 Connection: Keep-Alive 来保持连接。保持 TCP 连接可以省去下次请求时需要建立连接的时间,提升资源加载速度。
重定向
如果是一个需要重定向的网络请求,那么会涉及到重定向操作。响应中有 3xx 的状态码的情况一般就涉及到重定向了。
重定向的目标地址在响应头的 Location 中,浏览器会自动跳转到 Location 提供的地址再次发起一次 HTTP 请求。
如何确定用户登录信息
通过 cookie 或者 header 传递用户 uid 或者 token 之类的随机码。
HTTP 请求从发起到结束:
- 构建请求
- 查找缓存
- 准备 IP 和端口
- 等到 TCP 队列
- 建立 TCP 连接
- 发起 HTTP 请求
- 服务器处理请求
- 服务器返回请求
- 断开连接
如何排查请求过慢的问题
1 首先猜测最可能的出问题的地方,网络传输丢包比较严重,需要不断重传。然后通过ping curl看看对应的时延高不高。
2 然后通过wireshake看看具体哪里出了问题。
3 假如别人访问很快,自己电脑很慢,就要看看自己客户端是否有问题了。
浏览器访问网页需要各个进程间的通力协作
- 浏览器进程主要负责用户交互、子进程管理和文件存储等功能。
- 网络进程是面向渲染进程和浏览器进程等提供网络下载功能。
- 渲染进程是吧从网络下载的 HTML、JavaScript、CSS、图片等资源进行解析为可以显示和交互的页面。
在浏览器里,从输入 URL 到页面展示,中间发生了什么?
- 打开浏览器
- 用户在地址栏输入内容
- 解析输入内容
有效 URL :在 URL 前面加上协议头。
文本内容:使用浏览器默认搜索引擎,合成带搜索关键字的搜索引擎 URL。
- 回车
tips:在页面替换前会调用一次 beforeunload 事件允许页面在退出前执行一些操作。
- url 请求
把 URL 通过 IPC 发送到网络进程
查找本地缓存资源
没有缓存,进行 DNS 解析获取域名对应 IP
如果是 HTTPS,那么需要建立 TLS 连接。
建立 TCP 连接
发送 HTTP 请求
收到 HTTP 响应
响应内容解析
- 如果存在重定向行为 —— 出现 301、302 之类信息,根据 Location 再次发起新的 HTTP 或 HTTPS 请求。
- 响应数据类型处理 —— 通过 Content-Type 这个 HTTP 头来告诉浏览器返回的响应体数据类型,并且浏览器会根据 Content-Type 的值来决定如何显示响应体的内容。
text/html 展示 HTML 页面
application/octet-stream 显示数据为字节流类型,浏览器会按照下载类型来处理。
不同的 Content-Type 浏览器会有不同的后续操作
下载类型:浏览器会进行下载操作
内容类型:像图片、文本这些,会直接解析展示。
HTML 类型:那么就会有后续的操作~
- 准备渲染进程
通常情况下每个页面都会被分配到一个渲染进程。
如果符合“同一站点”策略,那么多个标签页会共用一个渲染进程。
- 提交文档 —— 将网络进程接收到的 HTML 数据提交给渲染进程。
浏览器进程收到网络进程数据,告诉渲染进程提交文档。
渲染进程和网络进程建立传输数据的管道
传输完成后,渲染进程告诉浏览器进程确认提交。
浏览器收到确认提交消息,更新浏览器界面状态,并更新 Web 页面。
PS:这里包含了很多教程间的通信啊,浏览器并非一个整体啊。到这里浏览器 UI 已经发生了改变(先请求数据,再更改浏览器 UI,再渲染 web 页面)。
渲染阶段
HTML 文档解析
构建 DOM 树
计算 CSS 属性
合并 CSSOM
排版
绘制
合并
………
默认端口号
HTTP 是 80,HTTPS 是 443。
tips
其实看课程评论也非常精彩的!
页面渲染是什么?
HTML 定义页面元素和结构
CSS 定义页面元素的样式。
JavaScript 可以对网页内容进行操作。
渲染流水线
构建 DOM 树
将 HTML 转换为浏览器能够理解的接口:DOM 树
样式计算
计算出每个元素的具体样式
把 CSS 转换为浏览器能够理解的结构
获取 CSS 的途径
- 通过 link 引用外部 CSS 文件
- 内嵌 CSS 样式
- 内联 CSS 样式
浏览器无法直接理解纯文本的 CSS 样式,所以渲染引擎会将 CSS 文本转换为浏览器可以理解的结构 styleSheets
转换样式表中的属性值,使其标准化。像:12px,rgb(0, 0, 0)等等。
计算出 DOM 树中每个节点的具体样式。
CSS 继承:每个 DOM 节点都包含有父节点的样式。
CSS 层叠:定义了如何合并来自多个源的属性值的算法。
如果不提供任何样式,浏览器会使用 UserAgent 默认样式。
布局阶段
计算出 DOM 树中可见元素的几何位置。
创建布局树 —— 在显示之前,要额外构建移柯只包含可见元素的布局树。
遍历 DOM 树中的所有课件节点,并把这些节点加到布局树中;
布局计算 —— 计算布局树节点的坐标位置。
分层
区分图层,参考 PS
渲染引擎需要为特定的节点生成专用的图层,并生成一颗对应的图层树。
类似于 PS,浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面。
并非布局树的每个节点都包含一个图层,如果节点没有对应的层,就会合并到父级。
什么情况下会有单独图层?
- 拥有层叠上下文属性的元素会被提升为单独一层
- 需要剪裁的地方会被创建图层。
图层绘制
所有的属性绘制都是通过一条条单独的指令去实现的。
栅格化
在做完图层布局、分层、绘制一系列操作后,渲染进程会将操作指令提交给合成线程。
合成线程会将图层划分为图块。(一个页面也许会很长,那么会被分割成很多固定宽高的图块。)
合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。
栅格化过程通常会使用 GPU 来加速生成。
合成
当所有位图被光栅化,合成线程会生成一个绘制图块的命令,提交给浏览器进程。
这时候会可以看到漂亮的页面啦~
渲染过程
将 HTML 转为 DOM 树
将 CSS 转为 styleSheets,计算出 DOM 节点的样式。
创建布局树,计算元素的布局信息。
对布局树分层,生成分层树。
为每个图层生成绘制列表,并将其提交给合成线程。
合成线程将图层分成图块,并在光栅化线程池中将图块转换为位图。
合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
浏览器进程根据 DrawQuad 消息生成页面,并显示到浏览器上。
重排、重绘和合成
重排:当页面的几何位置属性被改变时,如高度、宽度等,那么浏览器触发重新布局。
重绘:当页面只是被修改了颜色,那么布局阶段将不会被执行。
重绘省去了布局和分层的阶段,执行效率比重排操作要高。
合成:如果使用了 CSS 里面的 transform 属性来实现动画效果,它既不重排、也不重绘。而是在非主线程上执行了合成动画的效果。所以,合成能大大提升绘制效率。
减少重排重绘
- 使用 class 操作样式,而不是频繁操作 style
- 避免使用 table 布局
- 批量dom 操作,例如 createDocumentFragment,或者使用框架,例如 React
- Debounce window resize 事件
- 对 dom 属性的读写要分离
- will-change: transform 做优化