-
判断输入的是 URL 还是搜索的关键字
-
转换非 ASCII 的 Unicode 字符
- 比如:中文、空格
-
检查HSTS 列表
- 浏览器检查自带的“预加载 HSTS(HTTP严格传输安全)”列表,这个列表里包含了那些请求浏览器只使用HTTPS进行连接的 URL,如果这个 URL在列表里,则默认使用 HTTPS 协议而不是 HTTP 协议
-
查找域名的 IP 地址
- 查找浏览器的 DNS 缓存,没找到则执行下一步
- 查找操作系统中的 DNS 缓存,没找到则执行下一步
- 查找 OS 中的 hosts 文件,没找到则执行下一步
- 在 OS 的 LDNS 中查找域名对应的 IP(这一步可能会遇到 DNS 污染,解决方案为:1. 切换 DNS 服务器;2. 清空 DNS 缓存;3. 修改系统 hosts 文件,将域名与对应的 IP 地址绑定写死),没找到则发起一个迭代 DNS 解析请求
- LDNS 向 根域名服务器 发起请求,RDNS 返回 一级域名 对应的 IP 地址
- LDNS 向 一级域名服务器 发起请求,得到 二级域名 对应的 IP 地址
- LDNS 向 二级域名服务器 发起请求,得到 三级域名 对应的 IP 地址
- LDNS 将得到的 IP 地址返回给 OS,并且将这个 IP 地址缓存起来
- OS 将 IP 地址返回给浏览器,并且将这个 IP 地址缓存起来
- 浏览器得到域名对应的 IP 地址,并且将这个 IP 地址缓存起来
-
建立 TCP 连接
- 三次握手
- 第一次:浏览器 向 服务器 发送一个SYN 报文,表示请求建立连接,此时处于 SYN_SENT 状态
- 第二次:服务器 得到 SYN 报文,回应一个 SYN + ACK 报文,此时处于 SYN_REVD 状态
- 第三次:浏览器 收到 SYN + ACK 报文,回应一个 ACK 报文,处于 ESTABLISHED 状态
- 服务器收到 ACK 报文后,也处于 ESTABLISHED 状态
- 三次握手的目的是为了确认双方的 接受能力 和 发送能力是否正常
- 如果是 HTTPS 协议,三次握手过程还会进行数字证书验证以及加密密匙的生成
- 浏览器发送一个 ClientHello 消息到服务器,消息中包含了它的 TLS(Transport Layer Security)版本,可用的加密算法和压缩算法
- 服务端向浏览器返回一个 ServerHello 消息,消息中包含了服务端的 TLS 版本,服务器所选择的加密和压缩算法,以及数字证书认证机构(CA)签发的服务器公开证书,证书中包含了钥,浏览器会使用这个公钥加密接下来的握手过程,直到协商生成新的对称密匙
- 浏览器根据自己信任的 CA 列表,验证服务器的证书是否可信,若是可信,浏览器会生成一串伪随机数,使用服务器的公钥加密他,这串随机数被用于生成新的对称密钥
- 服务端使用自己的私钥解密上面的随机数,然后使用这串随机数生成自己的对称主密钥
- 浏览器发送一个 Finished 消息给服务端,使用对称密钥加密这次通讯的一个散列值
- 服务端生成自己的 hash 值,然后解密客户端发送来的消息,检查这两个值是否对应,如果对应,就向浏览器发送一个 Finished 消息,并使用协商好的对称密钥加密
- 至此,接下来整个会话都是用对称密钥加密,传输内容
- 三次握手
-
发送请求与接收响应
- 浏览器 向 服务器 发起一个 http-get 请求,请求中包含 URL、连接方式、User-Agent、cookie等信息
- 缓存具体行为在文章底部
- 服务器接收请求
- 鉴权,判断用户是否可以访问该资源
- 将对应的 HTML 文件返回给浏览器
- 若是后端架构为:代理服务器-服务器集群,则会先通过代理服务器进行端口转发、负载均衡等一系列动作,目的是为了降低服务器压力。
-
解析 HTML & 页面渲染
- 解析:HTML、CSS、JS
- 从浏览器进程的角度出发
- 浏览器进程有以下:
- Browser 进程:浏览器的主进程,负责主控,协调,只有一个
- 负责下载页面的网络文件
- 负责将 render 进程得到的存在内存中的位图渲染到页面上
- 负责创建和销毁 tab 进程
- 负责与用户交互
- GPU 进程:只有一个
- 负责 3D 绘制,只有当页面使用了硬件加速才会使用他,来渲染页面,否则不是用这个进程,而是用 Browser 进程来渲染页面
- render 进程:又叫做浏览器内核,每个 tab 页面对应一个独立的 render 进程,内部有多个线程
- render 进程是多线程的,例如:
- JS 引擎线程:
- 也叫 JS内核,解析 JS 脚本,执行代码
- 与 GUI 线程互斥,当 JS 引擎线程运行时,GUI 线程会被挂起,直到 JS 引擎线程结束运行,才会继续运行 GUI 线程
- 有一个主线程和多个 web worker 线程组成,由于 web worker 是附属于主线程,无法操作 dom 等,所以 JS 还是单线程语言(在主线程运行)
- GUI 渲染线程
- 用户解析 HTML 为 DOM 树,解析 CSS 为 CSSOM 树,布局 layout,绘制 paint
- 当页面需要 重排 reflow,重绘 repaint 时,使用该线程
- 于 JS 引擎线程互斥
- 事件触发线程
- 当对应事件触发(不论是 WebAPIs 完成事件触发,还是页面交互事件触发)时,该线程会将事件对应的回调函数放入 callback queue(任务队列)中,等待 JS 引擎线程处理。
- 定时触发线程
- 对应 setTimeout,setInterval,由该线程来计时,当计时结束,将事件对应的回调函数放入任务队列中
- 当 setTimeout 的定时小于 4ms,一律按 4ms 计算
- http 请求线程
- 每由一个 http 请求就开一个该线程
- 当检测到状态变更时,就会产生一个状态变更事件,如果该状态变更事件有对应回调函数的话,则放入任务队列中
- 任务队列轮询线程
- 用于轮询监听任务队列,以知道任务队列是否为空
- JS 引擎线程:
- render 进程是多线程的,例如:
- Browser 进程:浏览器的主进程,负责主控,协调,只有一个
- 浏览器进程有以下:
- 在解析过程中,可能会请求图片、外部 CSS、JS 文件,会开启一个 http 线程,建立 TCP 连接,发送请求与接收响应
- 解析的过程可能会反复很多次,因为 JS 可能会修改 DOM
- 解析 HTML 生成 DOM 树
- 解析 CSS 生成 CSS 树
- 调用 render 进程的 GUI 线程 来解析 HTML 和 CSS 为 DOM 树和 CSSOM 树
- 解析 HTML 时,遇到 script 标签,先判断是内嵌代码还是外部JS文件,内嵌代码直接开启 JS 引擎线程,挂起 GUI 线程,执行 JS 代码,如果是外部 JS 文件,将会开启 http 线程下载 JS 文件并执行
- 从浏览器进程的角度出发
- 渲染:构建 样式树 -> 渲染 -> 布局 -> 绘制
- 根据 CSS 树和 DOM 树 来创建 渲染树
- 渲染树完成后进行布局处理,确定每个节点在屏幕上的显示位置。
- 绘制
- 解析:HTML、CSS、JS
-
断开连接
- 四次挥手
- 第一次:浏览器 向 服务器发送一个 FIN 报文,处于 FIN_WAIT_1 状态
- 第二次:服务器收到 FIN 报文后,发送 ACK 报文给浏览器,表示已经收到浏览器的报文,此时处于 CLOSE_WAIT 状态
- 第三次:若此时服务器也想断开连接,和浏览器第一次挥手一样,发送一个 FIN 报文,此时处于 LAST_ACK 状态
- 第四次:浏览器收到 FIN 之后,发送一个 ACK 报文作为应答,此时客户端处于 TIME_WAIT 状态,等待一段时间确保服务器收到自己的 ACK 报文后进入 CLOSED 状态
- 服务器收到 ACK 报文后,就会断开连接,处于 CLOSED 状态
- 四次挥手
-
浏览器缓存分类:
- 强缓存
- 协议缓存
-
浏览器在加载资源时,会根据 request header 中的 expires 和 cache-control 判断是否命中强缓存
-
如果没有命中强缓存,client 将会向 server 发送请求,通过 last-modified 和 etag 验证资源是否命中协议缓存,若命中协议缓存,server 将会返回请求,但不会返回任何数据,资源依旧从缓存中读取
-
相同点
- 如果命中,都是从 client 加载资源,不从服务器加载
-
不同点
- 强缓存不发请求,协议缓存发送请求
-
强缓存
- 通过 Expires 和 Cache-Control 两种 request header 实现
- Expires
- Expires 表示资源过期时间,是一个绝对时间,由服务器返回。Expires 通过本地时间判断,也就是说,如果修改了本地时间,可能会造成缓存失效或者无法得到更新的资源
- Cacha-Control
- publi 表示资源可以被任何对象缓存,比如:client、代理服务器
- private 表示只能被单个用户换粗,不可共享缓存,即代理服务器不可缓存
- no-cache 表示发布缓存副本之前,强制要求原始服务器校验缓存
- no-store 表示不缓存
- max-age 表示缓存存储的最大周期,超过此时间就认为缓存过期,这个时间是相对时间
- 优先级高于 Expires
-
协议缓存
- 当强缓存没有命中时,就会发送请求到服务器,验证协议缓存是否命中,如果协议缓存命中,请求响应返回的 http 状态码为 304,并且会显示 Not Modified
- 协议缓存通过 Last-Modified、If-Modified-Since 和 ETag、If-None-Match 这两对 Header 来管理的
- Last-Modified、If-Modified-Since
- Last-Modified 表示本地文件最后修改日期,浏览器会在 request header 加上 If-Modified-Since (上次返回的 Last-Modified 的值),询问服务器在该日期后资源是否更新,有更新的话就会返回新资源
- ETag、If-None-Match
- Etag 描述了资源的信息摘要值,如果资源发生了变化,那么 Etag 的值也会变化,跟修改时间无关,Etag 保证每个资源都是唯一。
- If-None-Match 会将上次的 Etag 发送给服务器,服务器将 两次 Etag 值进行对比,若是不相同,则返回新的资源
- Etag 优先级高于 Last-Modified
待补充:
- CSS GPU 加速
- 客户端含有 Service Worker 的缓存情况