【前端知识体系】从输入URL到页面加载的过程?|8月更文挑战

·  阅读 224
【前端知识体系】从输入URL到页面加载的过程?|8月更文挑战

目录

  1. 梳理主干流程
  2. 从浏览器接收url到开启网络请求线程 【多进程的浏览器与单线程的JS引擎】
  3. 开启网络线程到发出一个完整的http请求 【在一个进程中开多个线程】
  4. 后台的处理
  5. 后台和前台的http交互
  6. http的缓存【强缓存与协商缓存】
  7. 解析页面流程
  8. 其他
  • JS引擎解析过程
  • 跨域
  • web安全
  • 垃圾回收
  • JS执行时的变量活动对象AO VO对象

一 梳理主干流程

  1. 从浏览器接收url到开启网络请求线程(这一部分可以展开浏览器的机制以及进程与线程之间的关系)

  2. 开启网络线程到发出一个完整的http请求(这一部分涉及到dns查询,tcp/ip请求,五层因特网协议栈等知识)

  3. 从服务器接收到请求到对应后台接收到请求(这一部分可能涉及到负载均衡,安全拦截以及后台内部的处理等等)

  4. 后台和前台的http交互(这一部分包括http头部、响应码、报文结构、cookie等知识,可以提下静态资源的cookie优化,以及编码解码,如gzip压缩等)

  5. 单独拎出来的缓存问题,http的缓存(这部分包括http缓存头部,etag,catch-control等)

  6. 浏览器接收到http数据包后的解析流程(解析html-词法分析然后解析成dom树、解析css生成css规则树、合并成render树,然后layout、painting渲染、复合图层的合成、GPU绘制、外链资源的处理、loaded和domcontentloaded等)

  7. CSS的可视化格式模型(元素的渲染规则,如包含块,控制框,BFC,IFC等概念)

  8. JS引擎解析过程(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链、回收机制等等)

  9. 其它(可以拓展不同的知识模块,如跨域,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的执行,事件的循环,都在这个进程内进行。接下来重点分析这个进程

请牢记,浏览器的渲染进程是多线程的

终于到了线程这个概念了?,好亲切。那么接下来看看它都包含了哪些线程(列举一些主要常驻线程):

  1. GUI渲染线程

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

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

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

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

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

三 开启网络线程到发出一个完整的http请求

这一部分主要内容包括:dns查询,tcp/ip请求构建,五层因特网协议栈等等

1) DNS查询得到IP

可做DNS预取优化

2) tcp/IP请求

http的本质就是tcp/ip请求

需要了解3次握手规则建立连接以及断开连接时的四次挥手

tcp将http长报文划分为短报文,通过三次握手与服务端建立连接,进行可靠传输

下方两图少了seq等细节报头。只看大概即可

image.png

image.png

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.1http1.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-SinceLast-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-MatchE-tag匹配,则代表内容未变,通知浏览器使用本地缓存,和Last-Modified不同,E-tag更精确,它是类似于指纹一样的东西,基于FileEtag INode Mtime Size生成,也就是说,只要文件变,指纹就会变,而且没有1s精确度的限制。

Max-Age相比Expires?

Expires使用的是服务器端的时间

但是有时候会有这样一种情况-客户端时间和服务端不同步

那这样,可能就会出问题了,造成了浏览器本地的缓存无用或者一直无法过期

所以一般http1.1后不推荐使用Expires

Max-Age使用的是客户端本地时间的计算,因此不会有这个问题

因此推荐使用Max-Age

注意,如果同时启用了Cache-ControlExpiresCache-Control优先级高。

E-tag相比Last-Modified?

Last-Modified

  • 表明服务端的文件最后何时改变的
  • 它有一个缺陷就是只能精确到1s,
  • 然后还有一个问题就是有的服务端的文件会周期性的改变,导致缓存失效

E-tag

  • 是一种指纹机制,代表文件相关指纹
  • 只有文件变才会变,也只要文件变就会变,
  • 也没有精确时间的限制,只要文件一遍,立马E-tag就不一样了

如果同时带有E-tagLast-Modified,服务端会优先检查E-tag

各大缓存头部的整体关系如下图

image.png

七 解析页面流程

  1. 解析HTML,构建DOM树

  2. 解析CSS,生成CSS规则树

  3. 合并DOM树和CSS规则,生成render树

  4. 布局render树(Layout/reflow),负责各元素尺寸、位置的计算

  5. 绘制render树(paint),绘制页面像素信息

  6. 浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上

image.png

其他

  • JS引擎解析过程
  • 跨域
  • web安全
  • 垃圾回收
  • JS执行时的变量活动对象AO VO对象
分类:
前端
分类:
前端
收藏成功!
已添加到「」, 点击更改