HTTP 阶段
- 输入URL(或者单击链接)
- 查看浏览器缓存,如果缓存且新鲜,则加载指定资源。(以下为未缓存的情况)
- 浏览器解析URL获取协议,主机,端⼝,路径
- 以 juejin.cn/post/695973… 为例
- 协议 : https
- 主机 :juejin.cn
- 端口 : 这里隐藏了,实际上使用的是 https 默认端口 443
- 路径 : post/6959730845611458596
- 浏览器将解析的数据组装到⼀个HTTP(GET)请求报⽂
- 浏览器获取主机的 ip 地址
- 浏览器缓存
- 本机缓存
- hosts⽂件
- 路由器缓存
- ISP DNS缓存
- DNS递归查询(可能存在负载均衡导致每次IP不⼀样)
- TCP 三次握手
- 大致流程如下
- 客户端发送⼀个TCP的SYN=1,seq=x的包到服务器端⼝
- 服务器发回SYN=1, ack=x+1, seq=Y的响应包
- 客户端发送ack=y+1, seq=x+1
- TCP链接建⽴后发送HTTP请求
- 服务器接受请求并解析,将请求转发到服务程序。(后端开始执行对应的代码)
- 服务器检查HTTP请求头是否包含缓存验证信息如果验证缓存新鲜,返回304等对应状态码
-
这里又来了个缓存可能有小伙伴不太明白,不是之前已经判断过缓存了吗。
-
当浏览器缓存未命中时,浏览器会返回缓存标记,发送请求时会携带标记,服务器判断标记新鲜,返回304状态码,浏览器会重新获取改请求的缓存。
-
图示如下:
-
- 处理程序读取完整请求并准备HTTP响应(后端程序处理完毕)
- 服务器将响应报⽂通过TCP连接发送回浏览器
- 浏览器接收HTTP响应,然后根据情况选择关闭TCP连接或者保留重⽤ (请求头:keep-alive 可保留请求重用,保留重用可减少三次握手与四次挥手的此时。目前 http1.1 和 http2 默认开启)
- 关闭TCP连接的四次握⼿如下:
- 主动⽅发送Fin=1, Seq= u报⽂
- 被动⽅发送ack=u+1, seq=v报⽂
- 被动⽅发送Fin=1, ack=u+1, Seq=w报⽂
- 主动⽅发送ack=w+1, seq=u+1报⽂
- 浏览器检查响应状态吗:是否为1XX,3XX, 4XX, 5XX,这些情况处理与2XX不同
- 如果资源可缓存,进⾏缓存,并对资源解码 (例如gzip压缩)(如果之前第一步有缓存资源则直接条到这一步)
前端页面部分
- 解析HTML⽂档,构件文档对象模型(DOM)。解析 JS 代码。页面中这两步骤可能会交替执行多次。
- DOM 树如下方代码 :
<!dOCTYPE HTML>
<html>
<head>
<title>web app </title>
<style>
#logo{color: red;}
<style>
</head>
<body>
<div id="logo"></div>
<script>
function fun()()
{
...
}
</script>
</body>
</html>
值得注意的是浏览器有纠错机制,例如放在 head 里的 div 标签会被放在 body 中。
-
解析过程中遇到图⽚、样式表、js⽂件,启动下载
-
构建CSSOM树:
- Tokenizing:字符流转换为标记流
- Node:根据标记创建节点
- CSSOM:节点创建CSSOM树
-
根据DOM树和CSSOM树构建渲染树。
-
开始渲染,渲染流程: 布局(Layout) -> 绘制(Paint)-> 复合(Composite)
-
布局(Layout)
- 计算每个节点精确的位置与大小 - "盒模型"
-
绘制(Paint)
- 像素化每个节点
-
复合(Composite)
- 浏览器会将页面拆解为不同的图层。
- 修改样式时,可以只修改一个图层的样式,并单独进行绘制。
- 将不同的图层复合成最终网页显示效果。
-
JS代码执行
-
浏览器创建Document对象并解析HTML,将解析到的元素和⽂本节点添加到⽂档中,此时document.readystate为loading
-
HTML解析器遇到没有async和defer的script时,将他们添加到⽂档中,然后执⾏⾏内或外部脚本。
-
JS代码被执行时,渲染引擎会停止工作,即停止渲染等待JS代码执行完。这也是为什么script标签要放最后的原因。
-
当解析器遇到设置了async属性的script时,可以下载脚本文件与渲染同时执行。
-
当⽂档完成解析,document.readState变成interactive
-
浏览器在Document对象上触发DOMContentLoaded事件
-
浏览器事件处理。
- 页面构建阶段,浏览器还会注册事件监听器,使我们的应用拥有交互能力。
- 所有已经生成的事件都添加到同一个事件队列中,以它们被浏览器检测到的顺序排列。
- 执行过程:
-
浏览器检查事件队列头。
-
如果浏览器没有在队列中检测到事件,则继续检查。
-
如果浏览器在队列头中检查到事件,则取出该事件并执行相应的事件处理器。
-
值得注意的时,浏览器一次只能同时执行一个事件,其余事件需要等待此事件执行完。因此不建议一个事件执行时间过长。
-
至此浏览器已经成功显示出正确的页面,并且具备交互能力。