目录
- 梳理主干流程
- 从浏览器接收url到开启网络请求线程 【多进程的浏览器与单线程的JS引擎】
- 开启网络线程到发出一个完整的http请求 【在一个进程中开多个线程】
- 后台的处理
- 后台和前台的http交互
- http的缓存【强缓存与协商缓存】
- 解析页面流程
- 其他
- JS引擎解析过程
- 跨域
- web安全
- 垃圾回收
- JS执行时的变量活动对象AO VO对象
一 梳理主干流程
-
从浏览器接收url到开启网络请求线程(这一部分可以展开浏览器的机制以及进程与线程之间的关系)
-
开启网络线程到发出一个完整的http请求(这一部分涉及到dns查询,tcp/ip请求,五层因特网协议栈等知识)
-
从服务器接收到请求到对应后台接收到请求(这一部分可能涉及到负载均衡,安全拦截以及后台内部的处理等等)
-
后台和前台的http交互(这一部分包括http头部、响应码、报文结构、cookie等知识,可以提下静态资源的cookie优化,以及编码解码,如gzip压缩等)
-
单独拎出来的缓存问题,http的缓存(这部分包括http缓存头部,etag,catch-control等)
-
浏览器接收到http数据包后的解析流程(解析html-词法分析然后解析成dom树、解析css生成css规则树、合并成render树,然后layout、painting渲染、复合图层的合成、GPU绘制、外链资源的处理、loaded和domcontentloaded等)
-
CSS的可视化格式模型(元素的渲染规则,如包含块,控制框,BFC,IFC等概念)
-
JS引擎解析过程(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链、回收机制等等)
-
其它(可以拓展不同的知识模块,如跨域,web安全,hybrid模式等等内容)
二 从浏览器接收url到开启网络请求线程
- 多进程的浏览器【一个tab页面一个进程】
- 一个Tab页面(浏览器渲染进程即内核)进程包括: 5大线程【JS线程,GUI线程,事件线程,网络请求线程】
1) 多进程的浏览器
浏览器是多进程的,有一个主控进程,以及每一个tab页面都会新开一个进程(某些情况下多个tab会合并进程)
进程可能包括主控进程,插件进程,GPU,tab页(浏览器内核)等等
- Browser进程:浏览器的主进程(负责协调、主控),只有一个
- 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
- GPU进程:最多一个,用于3D绘制
- 浏览器渲染进程(内核):默认每个Tab页面一个进程,互不影响,控制页面渲染,脚本执行,事件处理等(有时候会优化,如多个空白tab会合并成一个进程)
2)多线程的浏览器内核
每一个tab页面可以看作是浏览器内核进程,然后这个进程是多线程的,它有几大类子线程
- GUI线程
- JS引擎线程
- 事件触发线程
- 定时器线程
- 网络请求线程
JS引擎是内核进程中的一个线程,这也是为什么常说JS引擎是单线程的
3) 重点是浏览器内核(渲染进程)
重点来了,我们可以看到,上面提到了这么多的进程,那么,对于普通的前端操作来说,最终要的是什么呢?答案是渲染进程
可以这样理解,页面的渲染,JS的执行,事件的循环,都在这个进程内进行。接下来重点分析这个进程
请牢记,浏览器的渲染进程是多线程的
终于到了线程这个概念了?,好亲切。那么接下来看看它都包含了哪些线程(列举一些主要常驻线程):
-
GUI渲染线程
- 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
- 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
- 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
-
JS引擎线程
- 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
- JS引擎线程负责解析Javascript脚本,运行代码。
- JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
- 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
-
事件触发线程
- 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
- 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
- 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
- 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
-
定时触发器线程
- 传说中的
setInterval
与setTimeout
所在线程 - 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
- 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
- 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
- 传说中的
-
异步http请求线程
- 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
- 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。
三 开启网络线程到发出一个完整的http请求
这一部分主要内容包括:dns
查询,tcp/ip
请求构建,五层因特网协议栈
等等
1) DNS查询得到IP
可做DNS预取优化
2) tcp/IP请求
http的本质就是tcp/ip
请求
需要了解3次握手规则建立连接以及断开连接时的四次挥手
tcp将http长报文划分为短报文,通过三次握手与服务端建立连接,进行可靠传输
下方两图少了seq等细节报头。只看大概即可
3) http 0.9 到 http2 tcp与http请求数量的变化
tcp/ip的并发限制
浏览器对同一域名下并发的tcp连接是有限制的(2-10个不等)
而且在http1.0中往往一个资源下载就需要对应一个tcp/ip请求 所以针对这个瓶颈,又出现了很多的资源优化方案
- http0.9 只有get请求
- http1.0 中建立一个tcp连接只能发送一个http请求,导致需要频繁的连接断开tcp
- http1.1 【多路复用】中建立一个tcp连接只能发送多个http请求
- http2.0 同一个域名可以开始多个tcp连接,每个tcp下可发起多个http请求
4) 五层因特网协议栈
从应用层的发送http请求,到传输层通过三次握手建立tcp/ip连接,再到网络层的ip寻址,再到数据链路层的封装成帧,最后到物理层的利用物理介质传输。
1.应用层(dns,http) DNS解析成IP并发送http请求
2.传输层(tcp,udp) 建立tcp连接(三次握手)
3.网络层(IP,ARP) IP寻址
4.数据链路层(PPP) 封装成帧
5.物理层(利用物理介质传输比特流) 物理传输(然后传输的时候通过双绞线,电磁波等各种介质)
复制代码
当然,其实也有一个完整的OSI七层框架,与之相比,多了会话层、表示层。
OSI七层框架:物理层
、数据链路层
、网络层
、传输层
、会话层
、表示层
、应用层
表示层:主要处理两个通信系统中交换信息的表示方式,包括数据格式交换,数据加密与解密,数据压缩与终端类型转换等
会话层:它具体管理不同用户和进程之间的对话,如控制登陆和注销过程
复制代码
四 后台的处理
一般后台都是部署到容器中的,所以一般为:
- 先是容器接受到请求(如tomcat容器)
- 然后对应容器中的后台程序接收到请求(如java程序)
- 然后就是后台会有自己的统一处理,处理完后响应响应结果
概括下:
- 一般有的后端是有统一的验证的,如安全拦截,跨域验证
- 如果这一步不符合规则,就直接返回了相应的http报文(如拒绝请求等)
- 然后当验证通过后,才会进入实际的后台代码,此时是程序接收到请求,然后执行(譬如查询数据库,大量计算等等)
- 等程序执行完毕后,就会返回一个http响应包(一般这一步也会经过多层封装)
- 然后就是将这个包从后端发送到前端,完成交互
五 后台和前台的http交互
长连接与短连接
首先看tcp/ip
层面的定义:
- 长连接:一个tcp/ip连接上可以连续发送多个数据包,在tcp连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持(类似于心跳包)
- 短连接:通信双方有数据交互时,就建立一个tcp连接,数据发送完成后,则断开此tcp连接
然后在http层面:
http1.0
中,默认使用的是短连接,也就是说,浏览器没进行一次http操作,就建立一次连接,任务结束就中断连接,譬如每一个静态资源请求时都是一个单独的连接- http1.1起,默认使用长连接,使用长连接会有这一行
Connection: keep-alive
,在长连接的情况下,当一个网页打开完成后,客户端和服务端之间用于传输http的tcp连接不会关闭,如果客户端再次访问这个服务器的页面,会继续使用这一条已经建立的连接
注意: keep-alive不会永远保持,它有一个持续时间,一般在服务器中配置(如apache),另外长连接需要客户端和服务器都支持时才有效
http 2.0
http2.0不是https,它相当于是http的下一代规范(譬如https的请求可以是http2.0规范的)
然后简述下http2.0与http1.1的显著不同点:
- http1.1中,每请求一个资源,都是需要开启一个tcp/ip连接的,所以对应的结果是,每一个资源对应一个tcp/ip请求,由于tcp/ip本身有并发数限制,所以当资源一多,速度就显著慢下来
- http2.0中,一个tcp/ip请求可以请求多个资源,也就是说,只要一次tcp/ip请求,就可以请求若干个资源,分割成更小的帧请求,速度明显提升。
所以,如果http2.0全面应用,很多http1.1中的优化方案就无需用到了(譬如打包成精灵图,静态资源多域名拆分等)
然后简述下http2.0的一些特性:
- 多路复用(即一个tcp/ip连接可以请求多个资源)
- 首部压缩(http头部压缩,减少体积)
- 二进制分帧(在应用层跟传送层之间增加了一个二进制分帧层,改进传输性能,实现低延迟和高吞吐量)
- 服务器端推送(服务端可以对客户端的一个请求发出多个响应,可以主动通知客户端)
- 请求优先级(如果流被赋予了优先级,它就会基于这个优先级来处理,由服务器决定需要多少资源来处理该请求。)
https
https就是安全版本的http,譬如一些支付等操作基本都是基于https的,因为http请求的安全系数太低了。
简单来看,https与http的区别就是: 在请求前,会建立ssl链接,确保接下来的通信都是加密的,无法被轻易截取分析
一般来说,如果要将网站升级成https,需要后端支持(后端需要申请证书等),然后https的开销也比http要大(因为需要额外建立安全链接以及加密等),所以一般来说http2.0配合https的体验更佳(因为http2.0更快了)
一般来说,主要关注的就是SSL/TLS的握手流程,如下(简述):
1. 浏览器请求建立SSL链接,并向服务端发送一个随机数–Client random和客户端支持的加密方法,比如RSA加密,此时是明文传输。
2. 服务端从中选出一组加密算法与Hash算法,回复一个随机数–Server random,并将自己的身份信息以证书的形式发回给浏览器
(证书里包含了网站地址,非对称加密的公钥,以及证书颁发机构等信息)
3. 浏览器收到服务端的证书后
- 验证证书的合法性(颁发机构是否合法,证书中包含的网址是否和正在访问的一样),如果证书信任,则浏览器会显示一个小锁头,否则会有提示
- 用户接收证书后(不管信不信任),浏览会生产新的随机数–Premaster secret,然后证书中的公钥以及指定的加密方法加密`Premaster secret`,发送给服务器。
- 利用Client random、Server random和Premaster secret通过一定的算法生成HTTP链接数据传输的对称加密key-`session key`
- 使用约定好的HASH算法计算握手消息,并使用生成的`session key`对消息进行加密,最后将之前生成的所有信息发送给服务端。
4. 服务端收到浏览器的回复
- 利用已知的加解密方式与自己的私钥进行解密,获取`Premaster secret`
- 和浏览器相同规则生成`session key`
- 使用`session key`解密浏览器发来的握手消息,并验证Hash是否与浏览器发来的一致
- 使用`session key`加密一段握手消息,发送给浏览器
5. 浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,
复制代码
六 http的缓存【强缓存与协商缓存】
前后端的http交互中,使用缓存能很大程度上的提升效率,而且基本上对性能有要求的前端项目都是必用缓存的
强缓存与弱缓存
缓存可以简单的划分成两种类型:强缓存
(200 from cache
)与协商缓存
(304
)
区别简述如下:
- 强缓存(
200 from cache
)时,浏览器如果判断本地缓存未过期,就直接使用,无需发起http请求 - 协商缓存(
304
)时,浏览器会向服务端发起http请求,然后服务端告诉浏览器文件未改变,让浏览器使用本地缓存
对于协商缓存,使用Ctrl + F5
强制刷新可以使得缓存无效
但是对于强缓存,在未过期时,必须更新资源路径才能发起新的请求(更改了路径相当于是另一个资源了,这也是前端工程化中常用到的技巧)
缓存头部简述
上述提到了强缓存和协商缓存,那它们是怎么区分的呢?
答案是通过不同的http头部控制
先看下这几个头部:
If-None-Match/E-tag、If-Modified-Since/Last-Modified、Cache-Control/Max-Age、Pragma/Expires
复制代码
这些就是缓存中常用到的头部,这里不展开。仅列举下大致使用。
属于强缓存控制的:
(http1.1)Cache-Control/Max-Age
(http1.0)Pragma/Expires
复制代码
注意:Max-Age
不是一个头部,它是Cache-Control
头部的值
属于协商缓存控制的:
(http1.1)If-None-Match/E-tag
(http1.0)If-Modified-Since/Last-Modified
复制代码
可以看到,上述有提到http1.1
和http1.0
,这些不同的头部是属于不同http时期的
再提一点,其实HTML页面中也有一个meta标签可以控制缓存方案-Pragma
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
复制代码
不过,这种方案还是比较少用到,因为支持情况不佳,譬如缓存代理服务器肯定不支持,所以不推荐
头部的区别
首先明确,http的发展是从http1.0到http1.1
而在http1.1中,出了一些新内容,弥补了http1.0的不足。
http1.0中的缓存控制:
Pragma
:严格来说,它不属于专门的缓存控制头部,但是它设置no-cache
时可以让本地强缓存失效(属于编译控制,来实现特定的指令,主要是因为兼容http1.0,所以以前又被大量应用)Expires
:服务端配置的,属于强缓存,用来控制在规定的时间之前,浏览器不会发出请求,而是直接使用本地缓存,注意,Expires一般对应服务器端时间,如Expires:Fri, 30 Oct 1998 14:19:41
If-Modified-Since/Last-Modified
:这两个是成对出现的,属于协商缓存的内容,其中浏览器的头部是If-Modified-Since
,而服务端的是Last-Modified
,它的作用是,在发起请求时,如果If-Modified-Since
和Last-Modified
匹配,那么代表服务器资源并未改变,因此服务端不会返回资源实体,而是只返回头部,通知浏览器可以使用本地缓存。Last-Modified
,顾名思义,指的是文件最后的修改时间,而且只能精确到1s
以内
http1.1中的缓存控制:
Cache-Control
:缓存控制头部,有no-cache、max-age等多种取值Max-Age
:服务端配置的,用来控制强缓存,在规定的时间之内,浏览器无需发出请求,直接使用本地缓存,注意,Max-Age是Cache-Control头部的值,不是独立的头部,譬如Cache-Control: max-age=3600
,而且它值得是绝对时间,由浏览器自己计算If-None-Match/E-tag
:这两个是成对出现的,属于协商缓存的内容,其中浏览器的头部是If-None-Match
,而服务端的是E-tag
,同样,发出请求后,如果If-None-Match
和E-tag
匹配,则代表内容未变,通知浏览器使用本地缓存,和Last-Modified不同,E-tag更精确,它是类似于指纹一样的东西,基于FileEtag INode Mtime Size
生成,也就是说,只要文件变,指纹就会变,而且没有1s精确度的限制。
Max-Age相比Expires?
Expires
使用的是服务器端的时间
但是有时候会有这样一种情况-客户端时间和服务端不同步
那这样,可能就会出问题了,造成了浏览器本地的缓存无用或者一直无法过期
所以一般http1.1后不推荐使用Expires
而Max-Age
使用的是客户端本地时间的计算,因此不会有这个问题
因此推荐使用Max-Age
。
注意,如果同时启用了Cache-Control
与Expires
,Cache-Control
优先级高。
E-tag相比Last-Modified?
Last-Modified
:
- 表明服务端的文件最后何时改变的
- 它有一个缺陷就是只能精确到1s,
- 然后还有一个问题就是有的服务端的文件会周期性的改变,导致缓存失效
而E-tag
:
- 是一种指纹机制,代表文件相关指纹
- 只有文件变才会变,也只要文件变就会变,
- 也没有精确时间的限制,只要文件一遍,立马E-tag就不一样了
如果同时带有E-tag
和Last-Modified
,服务端会优先检查E-tag
各大缓存头部的整体关系如下图
七 解析页面流程
-
解析HTML,构建DOM树
-
解析CSS,生成CSS规则树
-
合并DOM树和CSS规则,生成render树
-
布局render树(Layout/reflow),负责各元素尺寸、位置的计算
-
绘制render树(paint),绘制页面像素信息
-
浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上
其他
- JS引擎解析过程
- 跨域
- web安全
- 垃圾回收
- JS执行时的变量活动对象AO VO对象