从输入url到页面完成加载发生了什么

01 DNS解析
02 TCP连接
03 发送HTTP请求
04 服务器处理请求并返回HTTP报文
05 浏览器解析渲染页面
06 连接结束
**

  • 从浏览器接收url到开启网络请求线程
    • 多进程的浏览器
    • 多线程的浏览器内核
    • 解析URL
    • 网络请求都是单独的线程
    • 更多
  • 开启网络线程到发出一个完整的http请求
    • DNS查询得到IP
    • tcp/ip请求
    • 五层因特网协议栈

从应用层的发送http请求,到传输层通过三次握手建立tcp/ip连接,再到网络层的ip寻址,再到数据链路层的封装成帧,最后到物理层的利用物理介质传输。

  • 从服务器接收到请求到对应后台接收到请求
    • 负载均衡
    • 后台的处理
  • 后台和前台的http交互
    • http报文结构
    • cookie以及优化
    • gzip压缩
    • 长连接与短连接
    • http 2.0
    • https
  • 单独拎出来的缓存问题,http的缓存
    • 强缓存与弱缓存
    • 缓存头部简述
    • 头部的区别
  • 解析页面流程
    • 流程简述
    • HTML解析,构建DOM
    • 生成CSS规则
    • 构建渲染树
    • 渲染
    • 简单层与复合层
    • Chrome中的调试
    • 资源外链的下载
    • loaded和domcontentloaded

image.png

  • CSS的可视化格式模型
    • 包含块(Containing Block)
    • 控制框(Controlling Box)
    • BFC(Block Formatting Context)
    • IFC(Inline Formatting Context)
    • 其它
  • JS引擎解析过程
    • JS的解释阶段
    • JS的预处理阶段
    • JS的执行阶段
    • 回收机制

00 TCP协议

而TCP协议就是为了解决UDP协议的缺点而诞生的,它虽然实现上比UDP协议复杂,但是可靠性好,可以保证数据被发送到目标设备上。

01 TCP三次握手

TCP协议是如何保证可靠性的呢?就是通过三次与目标设备的通信来确定数据包发送成功。以浏览器和服务器的通信来打比方:
浏览器:你好服务器,我是 浏览器A。
服务器:你好 浏览器A,我是 服务器B。
浏览器:服务器B 你好。

Http - 图2

TCP三次握手

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。这样就保证了,每次传送数据都会准确到达目标设备了。

03 浏览器解析渲染页面

04 TCP四次挥手

当数据包发送完毕需要断开连接的时候,就需要TCP的四次挥手来保证链接的合理断开。再次以浏览器和服务器的通信打比方:

主动结束方:你好,我的数据发送完毕了,我要进入准备断开的状态了。(此时它虽然不再发送数据了,但是可以接受数据)
另一方:我知道了,我还没有发送完毕的,你等着吧。
另一方:我也发送完毕了,可以断开链接了。(此时它也进入准备断开的状态)
主动结束方:好的,那断开吧。

官方描述

Http - 图3

TCP四次挥手
1.客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
2.服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
3.服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
4.客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。

这是前端面试中在设计HTTP协议问题时,经常会被问的一个问题。其实也不难理解,因为服务端的listen状态下的socket当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
通过TCP协议使得两台设备成功链接,并成功发送了数据,接下来,就需要服务器端来处理数据了。

渲染进程是多线程的,它主要包含了以下六个线程:

GUI渲染线程

负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。
注意⚠️:GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

JS引擎线程

(例如V8引擎)JS引擎线程负责解析Javascript脚本,运行代码。JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
注意:GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。

事件触发线程

归属于浏览器而不是JS引擎,用来控制事件循环。当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中,当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。
注意⚠️:由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

定时触发器线程

传说中的setInterval与setTimeout所在线程
浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。(也就是0ms也算4ms)

异步http请求线程

在XMLHttpRequest连接后是通过浏览器新开一个线程请求
将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

合成线程

在GUI渲染线程后执行,将GUI渲染线程生成的带绘制列表转换为位图。

IO线程

用来和其他进程进行通信

image.png

  • 线程在进程下行进(单纯的车厢无法运行)
  • 一个进程可以包含多个线程(一辆火车可以有多个车厢)
  • 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
  • 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
  • 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
  • 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
  • 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
  • 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-”互斥锁”
  • 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

浏览器为什么是多进程的

  • 大量网页转而使用动态网页
  • 渲染引擎在演化的过程中会频繁的出现bug,有些bug会导致渲染引擎崩溃
  • 渲染引擎会经常性的在网络上遇到不可信、甚至是恶意的代码,它们会利用这些漏洞在你的电脑上安装恶意的软件
  • 如果浏览器中的一个网络应用崩溃的话,这会波及所有被打开的应用在内的任何其他应用。单线程的网络应用不得不经常相互竞争以获得的cpu时间,这有时会导致整个浏览器无法响应

优点

  • 避免单个tab页影响整个浏览器
  • 避免插件影响整个浏览器
  • 多进程可以高效的利用多核优势
  • 方便使用沙盒模型隔离插件等进程,提高浏览器的稳定性

js为什么是单线程的

这主要和js的用途有关,js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom;这决定了它只能是单线程,否则会带来很复杂的同步问题。 举个例子:如果js被设计了多线程,如果有一个线程要修改一个dom元素,另一个线程要删除这个dom元素,此时浏览器就会一脸茫然,不知所措。所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线