1. 首页
  2. IT资讯

在浏览器输入 URL 回车之后发生了什么?(超详细版)

“u003Cpu003Eu003Cstrongu003E蓝色字体u003Cu002Fstrongu003E,选择“标星公众号”u003Cu002Fpu003Eu003Cpu003E优质文章,第一时间送达u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRbBH7dh8YhRwXK” img_width=”640″ img_height=”18″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003E作者 | 4Arku003Cu002Fpu003Eu003Cpu003E来源 | https:u002Fu002F4ark.meu002Fpostu002Fb6c7c0a2.htmlu003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E前言u003Cu002Fh1u003E这个问题已经是老生常谈了,更是经常被作为面试的压轴题出现,网上也有很多文章,但最近闲的无聊,然后就自己做了一篇笔记,感觉比之前理解更透彻了。u003Cpu003E这篇笔记是我这两天看了数十篇文章总结出来的,所以相对全面一点,但由于我是做前端的,所以会比较重点分析浏览器渲染页面那一部分,至于其他部分我会罗列出关键词,感兴趣的可以自行查阅.u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E注意:u003Cu002Fstrongu003E本文的步骤是建立在,请求的是一个简单的 HTTP 请求,没有 HTTPS、HTTP2、最简单的 DNS、没有代理、并且服务器没有任何问题的基础上,尽管这是不切实际的。u003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E大致流程u003Cu002Fh1u003Eu003Colu003Eu003Cliu003Eu003Cpu003EURL 解析u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EDNS 查询u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003ETCP 连接u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E处理请求u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E接受响应u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E渲染页面u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Folu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E一、URL 解析u003Cu002Fh1u003Eu003Cstrongu003E地址解析:u003Cu002Fstrongu003E首u003Cstrongu003E先u003Cu002Fstrongu003E判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。u003Cstrongu003EHSTSu003Cu002Fstrongu003E由于安全隐患,会使用 HSTS 强制客户端使用 HTTPS 访问页面。详见:你所不知道的 HSTSu003Csupu003E[1]u003Cu002Fsupu003E。u003Cstrongu003E其他操作u003Cu002Fstrongu003Eu003Cpu003E浏览器还会进行一些额外的操作,比如安全检查、访问限制(之前国产浏览器限制 996.icu)。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E检查缓存u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3HZ9KP5XqX” img_width=”1080″ img_height=”833″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E二、DNS 查询u003Cu002Fh1u003Eu003Cstrongu003E基本步骤u003Cu002Fstrongu003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3I2AKU3uNL” img_width=”1080″ img_height=”139″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003Eu003Cstrongu003E1. 浏览器缓存u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E浏览器会先检查是否在缓存中,没有则调用系统库函数进行查询。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E2. 操作系统缓存u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E操作系统也有自己的 DNS缓存,但在这之前,会向检查域名是否存在本地的 Hosts 文件里,没有则向 DNS 服务器发送查询请求。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E3. 路由器缓存u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E路由器也有自己的缓存。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E4. ISP DNS 缓存u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EISP DNS 就是在客户端电脑上设置的首选 DNS 服务器,它们在大多数情况下都会有缓存。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E根域名服务器查询u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E在前面所有步骤没有缓存的情况下,本地 DNS 服务器会将请求转发到互联网上的根域,下面这个图很好的诠释了整个流程:u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3IFG9ZiDht” img_width=”805″ img_height=”478″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003Eu003Cstrongu003E需要注意的点u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Colu003Eu003Cliu003Eu003Cpu003E递归方式:一路查下去中间不返回,得到最终结果才返回信息(浏览器到本地DNS服务器的过程)u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E迭代方式,就是本地DNS服务器到根域名服务器查询的方式。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E什么是 DNS 劫持u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E前端 dns-prefetch 优化u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Folu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E三、TCP 连接u003Cu002Fh1u003Eu003Cpu003ETCPu002FIP 分为四层,在发送数据时,每层都要对数据进行封装:u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3IZBNDoxLW” img_width=”1007″ img_height=”1027″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003Eu003Cstrongu003E1. 应用层:u003Cu002Fstrongu003Eu003Cstrongu003E发送 HTTP 请求u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003E在前面的步骤我们已经得到服务器的 IP 地址,浏览器会开始构造一个 HTTP 报文,其中包括:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E请求报头(Request Header):请求方法、目标地址、遵循的协议等等u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E请求主体(其他参数)u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E其中需要注意的点:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E浏览器只能发送 GET、POST 方法,而打开网页使用的是 GET 方法u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003Eu003Cstrongu003E2. 传输层:u003Cu002Fstrongu003Eu003Cstrongu003ETCP 传输报文u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003E传输层会发起一条到达服务器的 TCP 连接,为了方便传输,会对数据进行分割(以报文段为单位),并标记编号,方便服务器接受时能够准确地还原报文信息。u003Cu002Fpu003Eu003Cpu003E在建立连接前,会先进行 TCP 三次握手。u003Cu002Fpu003Eu003Cblockquoteu003E“u003Cpu003E关于 TCPu002FIP 三次握手,网上已经有很多段子和图片生动地描述了。u003Cu002Fpu003Eu003Cpu003E相关知识点:u003Cu002Fpu003Eu003Colu003Eu003Cliu003Eu003Cpu003ESYN 泛洪攻击u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Folu003E”u003Cu002Fblockquoteu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003Eu003Cstrongu003E3. 网络层:u003Cu002Fstrongu003Eu003Cstrongu003EIP协议查询Mac地址u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003E将数据段打包,并加入源及目标的IP地址,并且负责寻找传输路线。u003Cu002Fpu003Eu003Cpu003E判断目标地址是否与当前地址处于同一网络中,是的话直接根据 Mac 地址发送,否则使用路由表查找下一跳地址,以及使用 ARP 协议查询它的 Mac 地址。u003Cu002Fpu003Eu003Cblockquoteu003E“u003Cpu003E注意:在 OSI 参考模型中 ARP 协议位于链路层,但在 TCPu002FIP 中,它位于网络层。u003Cu002Fpu003E”u003Cu002Fblockquoteu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003Eu003Cstrongu003E4. 链路层:u003Cu002Fstrongu003Eu003Cstrongu003E以太网协议u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003Eu003Cstrongu003E以太网协议u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E根据以太网协议将数据分为以“帧”为单位的数据包,每一帧分为两个部分:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E标头:数据包的发送者、接受者、数据类型u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E数据:数据包具体内容u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrongu003EMac 地址u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E以太网规定了连入网络的所有设备都必须具备“网卡”接口,数据包都是从一块网卡传递到另一块网卡,网卡的地址就是 Mac 地址。每一个 Mac 地址都是独一无二的,具备了一对一的能力。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E广播u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E发送数据的方法很原始,直接把数据通过 ARP 协议,向本网络的所有机器发送,接收方根据标头信息与自身 Mac 地址比较,一致就接受,否则丢弃。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E注意u003Cu002Fstrongu003E:接收方回应是单播。u003Cu002Fpu003Eu003Cblockquoteu003E“u003Cpu003E相关知识点:u003Cu002Fpu003Eu003Colu003Eu003Cliu003Eu003Cpu003EARP 攻击u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Folu003E”u003Cu002Fblockquoteu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003E服务器接受请求u003Cu002Fh2u003Eu003Cpu003E接受过程就是把以上步骤逆转过来,参见上图。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E四、服务器处理请求u003Cu002Fh1u003Eu003Cstrongu003E大致流程u003Cu002Fstrongu003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3ckCvmMWeq” img_width=”1080″ img_height=”331″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003Eu003Cstrongu003EHTTPDu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E最常见的 HTTPD 有 Linux 上常用的 Apache 和 Nginx,以及 Windows 上的 IIS。u003Cu002Fpu003Eu003Cpu003E它会监听得到的请求,然后开启一个子进程去处理这个请求。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E处理请求u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E接受 TCP 报文后,会对连接进行处理,对HTTP协议进行解析(请求方法、域名、路径等),并且进行一些验证:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E验证是否配置虚拟主机u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E验证虚拟主机是否接受此方法u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E验证该用户可以使用该方法(根据 IP 地址、身份信息等)u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrongu003E重定向u003Cu002Fstrongu003E假如服务器配置了 HTTP 重定向,就会返回一个u003Ccodeu003E301u003Cu002Fcodeu003E永久重定向响应,浏览器就会根据响应,重新发送 HTTP 请求(重新执行上面的过程)。u003Cstrongu003EURL 重写u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E然后会查看 URL 重写规则,如果请求的文件是真实存在的,比如图片、html、css、js文件等,则会直接把这个文件返回。u003Cu002Fpu003Eu003Cpu003E否则服务器会按照规则把请求重写到 一个 REST 风格的 URL 上。u003Cu002Fpu003Eu003Cpu003E然后根据动态语言的脚本,来决定调用什么类型的动态文件解释器来处理这个请求。u003Cu002Fpu003Eu003Cpu003E以 PHP 语言的 MVC 框架举例,它首先会初始化一些环境的参数,根据 URL 由上到下地去匹配路由,然后让路由所定义的方法去处理请求。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E五、浏览器接受响应u003Cu002Fh1u003Eu003Cpu003E浏览器接收到来自服务器的响应资源后,会对资源进行分析。u003Cu002Fpu003Eu003Cpu003E首先查看 Response header,根据不同状态码做不同的事(比如上面提到的重定向)。u003Cu002Fpu003Eu003Cpu003E如果响应资源进行了压缩(比如 gzip),还需要进行解压。u003Cu002Fpu003Eu003Cpu003E然后,对响应资源做缓存。u003Cu002Fpu003Eu003Cpu003E接下来,根据响应资源里的 MIMEu003Csupu003E[3]u003Cu002Fsupu003E类型去解析响应内容(比如 HTML、Image各有不同的解析方式)。u003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E六、渲染页面u003Cu002Fh1u003Eu003Cpu003Eu003Cstrongu003E浏览器内核u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3d3HM4oIaS” img_width=”1080″ img_height=”516″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003E不同的浏览器内核,渲染过程也不完全相同,但大致流程都差不多。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E基本流程u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3dR9cz06hX” img_width=”1013″ img_height=”1024″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003E6.1. HTML 解析u003Cu002Fh2u003Eu003Cpu003E首先要知道浏览器解析是从上往下一行一行地解析的。u003Cu002Fpu003Eu003Cpu003E解析的过程可以分为四个步骤:u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E① 解码(encoding)u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E传输回来的其实都是一些二进制字节数据,浏览器需要根据文件指定编码(例如UTF-8)转换成字符串,也就是HTML 代码。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E② 预解析(pre-parsing)u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E预解析做的事情是提前加载资源,减少处理时间,它会识别一些会请求资源的属性,比如u003Ccodeu003Eimgu003Cu002Fcodeu003E标签的u003Ccodeu003Esrcu003Cu002Fcodeu003E属性,并将这个请求加到请求队列中。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E③ 符号化(Tokenization)u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E符号化是词法分析的过程,将输入解析成符号,HTML 符号包括,开始标签、结束标签、属性名和属性值。u003Cu002Fpu003Eu003Cpu003E它通过一个状态机去识别符号的状态,比如遇到u003Ccodeu003E<u003Cu002Fcodeu003E,u003Ccodeu003E>u003Cu002Fcodeu003E状态都会产生变化。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E④ 构建树(tree construction)u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cblockquoteu003E“u003Cpu003E注意:符号化和构建树是并行操作的,也就是说只要解析到一个开始标签,就会创建一个 DOM 节点。u003Cu002Fpu003E”u003Cu002Fblockquoteu003Eu003Cpu003E在上一步符号化中,解析器获得这些标记,然后以合适的方法创建u003Ccodeu003EDOMu003Cu002Fcodeu003E对象并把这些符号u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-3″u003E插入u003Cu002Fiu003E到u003Ccodeu003EDOMu003Cu002Fcodeu003E对象中。u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E<html>u003Cu002Fcodeu003Eu003Ccodeu003E<head>u003Cu002Fcodeu003Eu003Ccodeu003E <title>Web page parsing<u002Ftitle>u003Cu002Fcodeu003Eu003Ccodeu003E<u002Fhead>u003Cu002Fcodeu003Eu003Ccodeu003E<body>u003Cu002Fcodeu003Eu003Ccodeu003E <div>u003Cu002Fcodeu003Eu003Ccodeu003E <h1>Web page parsing<u002Fh1>u003Cu002Fcodeu003Eu003Ccodeu003E <p>This is an example Web page.<u002Fp>u003Cu002Fcodeu003Eu003Ccodeu003E <u002Fdiv>u003Cu002Fcodeu003Eu003Ccodeu003E<u002Fbody>u003Cu002Fcodeu003Eu003Ccodeu003E<u002Fhtml>u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3dhDodVZiZ” img_width=”651″ img_height=”428″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003Eu003Cstrongu003E浏览器容错进制u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E你从来没有在浏览器看过类似”语法无效”的错误,这是因为浏览器去纠正错误的语法,然后继续工作。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E事件u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E当整个解析的过程完成以后,浏览器会通过u003Ccodeu003EDOMContentLoadedu003Cu002Fcodeu003E事件来通知u003Ccodeu003EDOMu003Cu002Fcodeu003E解析完成。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003E6.2. CSS 解析u003Cu002Fh2u003Eu003Cpu003E一旦浏览器下载了 CSS,CSS 解析器就会处理它遇到的任何 CSS,根据语法规范u003Csupu003E[4]u003Cu002Fsupu003E解析出所有的 CSS 并进行标记化,然后我们得到一个规则表。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003ECSS 匹配规则u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E在匹配一个节点对应的 CSS 规则时,是按照从右到左的顺序的,例如:u003Ccodeu003Ediv p { font-size :14px }u003Cu002Fcodeu003E会先寻找所有的u003Ccodeu003Epu003Cu002Fcodeu003E标签然后判断它的父元素是否为u003Ccodeu003Edivu003Cu002Fcodeu003E。u003Cu002Fpu003Eu003Cpu003E所以我们写 CSS 时,尽量用 id 和 class,千万不要过度层叠。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003E6.3. 渲染树u003Cu002Fh2u003Eu003Cpu003E其实这就是一个 DOM 树和 CSS 规则树合并的过程。u003Cu002Fpu003Eu003Cblockquoteu003E“u003Cpu003E注意:渲染树会忽略那些不需要渲染的节点,比如设置了u003Ccodeu003Edisplay:noneu003Cu002Fcodeu003E的节点。u003Cu002Fpu003E”u003Cu002Fblockquoteu003Eu003Cpu003Eu003Cstrongu003E计算u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E通过计算让任何尺寸值都减少到三个可能之一:u003Ccodeu003Eautou003Cu002Fcodeu003E、百分比、px,比如把u003Ccodeu003Eremu003Cu002Fcodeu003E转化为u003Ccodeu003Epxu003Cu002Fcodeu003E。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E级联u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E浏览器需要一种方法来确定哪些样式才真正需要应用到对应元素,所以它使用一个叫做u003Ccodeu003Especificityu003Cu002Fcodeu003E的公式,这个公式会通过:u003Cu002Fpu003Eu003Colu003Eu003Cliu003Eu003Cpu003E标签名、class、idu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E是否内联样式u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Ccodeu003E!importantu003Cu002Fcodeu003Eu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Folu003Eu003Cpu003E然后得出一个权重值,取最高的那个。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E渲染阻塞u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E当遇到一个u003Ccodeu003Escriptu003Cu002Fcodeu003E标签时,DOM 构建会被暂停,直至脚本完成执行,然后继续构建 DOM 树。u003Cu002Fpu003Eu003Cpu003E但如果 JS 依赖 CSS 样式,而它还没有被下载和构建时,浏览器就会延迟脚本执行,直至 CSS Rules 被构建。u003Cu002Fpu003Eu003Cpu003E所有我们知道:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003ECSS 会阻塞 JS 执行u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EJS 会阻塞后面的 DOM 解析u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E为了避免这种情况,应该以下原则:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003ECSS 资源排在 JavaScript 资源前面u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EJS 放在 HTML 最底部,也就是 u003Ccodeu003E<u002Fbody>u003Cu002Fcodeu003E前u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E另外,如果要改变阻塞模式,可以使用 defer 与 async,详见:这篇文章u003Csupu003E[5]u003Cu002Fsupu003Eu003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003Eu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003E6.4u003Cu002Fiu003E. 布局与绘制u003Cu002Fh2u003Eu003Cpu003E确定渲染树种所有节点的几何属性,比如:位置、大小等等,最后输入一个盒子模型,它能精准地捕获到每个元素在屏幕内的准确位置与大小。u003Cu002Fpu003Eu003Cpu003E然后遍历渲染树,调用渲染器的 paint 方法在屏幕上显示其内容。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003E6.5. 合并渲染层u003Cu002Fh2u003Eu003Cpu003E把以上绘制的所有图片合并,最终输出一张图片。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003E6.6. 回流与重绘u003Cu002Fh2u003Eu003Cpu003Eu003Cstrongu003E回流(reflow)u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E当浏览器发现某个部分发现变化影响了布局时,需要倒回去重新渲染,会从u003Ccodeu003Ehtmlu003Cu002Fcodeu003E标签开始递归往下,重新计算位置和大小。u003Cu002Fpu003Eu003Cpu003Ereflow基本是无法避免的,因为当你滑动一下鼠标、resize 窗口,页面就会产生变化。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E重绘(repaint)u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E改变了某个元素的背景色、文字颜色等等不会影响周围元素的位置变化时,就会发生重绘。u003Cu002Fpu003Eu003Cpu003E每次重绘后,浏览器还需要合并渲染层并输出到屏幕上。u003Cu002Fpu003Eu003Cpu003E回流的成本要比重绘高很多,所以我们应该尽量避免产生回流。u003Cu002Fpu003Eu003Cpu003E比如:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003Eu003Ccodeu003Edisplay:noneu003Cu002Fcodeu003E会触发回流,而u003Ccodeu003Evisibility:hiu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003Eddu003Cu002Fiu003Eenu003Cu002Fcodeu003E只会触发重绘。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003E6.7. JavaScript 编译执行u003Cu002Fh2u003Eu003Cpu003Eu003Cstrongu003E大致流程u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3dw3YSNLFd” img_width=”1080″ img_height=”598″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003E可以分为三个阶段:u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003E1. 词法分析u003Cu002Fh2u003Eu003Cpu003EJS 脚本加载完毕后,会首先进入语法分析阶段,它首先会分析代码块的语法是否正确,不正确则抛出“语法错误”,停止执行。u003Cu002Fpu003Eu003Cpu003E几个步骤:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E分词,例如将u003Ccodeu003Evar a = 2u003Cu002Fcodeu003E,,分成u003Ccodeu003Evaru003Cu002Fcodeu003E、u003Ccodeu003Eau003Cu002Fcodeu003E、u003Ccodeu003E=u003Cu002Fcodeu003E、u003Ccodeu003E2u003Cu002Fcodeu003E这样的词法单元。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E解析,将词法单元转换成抽象语法树(AST)。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E代码生成,将抽象语法树转换成机器指令。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003E2. 预编译u003Cu002Fh2u003Eu003Cpu003EJS 有三种运行环境:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E全局环境u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E函数环境u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eevalu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E每进入一个不同的运行环境都会创建一个对应的执行上下文,根据不同的上下文环境,形成一个函数调用栈,栈底永远是全局执行上下文,栈顶则永远是当前执行上下文。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E创建执行上下文u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E创建执行上下文的过程中,主要做了以下三件事:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E创建变量对象u003Cu002Fpu003Eu003Cu002Fliu003Eu003Culu003Eu003Cliu003Eu003Cpu003E参数、函数、变量u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cliu003Eu003Cpu003E建立作用域链u003Cu002Fpu003Eu003Cu002Fliu003Eu003Culu003Eu003Cliu003Eu003Cpu003E确认当前执行环境是否能访问变量u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cliu003Eu003Cpu003E确定 This 指向u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003E3. 执行u003Cu002Fh2u003Eu003Cpu003Eu003Cstrongu003EJS 线程u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3pMVvYXyq” img_width=”1080″ img_height=”310″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003E虽然 JS 是单线程的,但实际上参与工作的线程一共有四个:u003Cu002Fpu003Eu003Cpu003E其中三个只是协助,只有 JS 引擎线程是真正执行的u003Cu002Fpu003Eu003Cpu003E其中三个只是协助,只有 JS 引擎线程是真正执行的u003Cu002Fpu003Eu003Cpu003E”u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003EJS 引擎线程:也叫 JS 内核,负责解析执行 JS 脚本程序的主线程,例如 V8 引擎u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E事件触发线程:属于浏览器内核线程,主要用于控制事件,例如鼠标、键盘等,当事件被触发时,就会把事件的处理函数推进事件队列,等待 JS 引擎线程执行u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003E定时器触发线程:主要控制u003Ccodeu003EsetIntervalu003Cu002Fcodeu003E和u003Ccodeu003EsetTimeoutu003Cu002Fcodeu003E,用来计时,计时完毕后,则把定时器的处理函数推进事件队列中,等待 JS 引擎线程。u003Cu002Fliu003Eu003Cliu003Eu003Cpu003EHTTP 异步请求线程:通过XMLHttpRequest连接后,通过浏览器新开的一个线程,监控readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进事件队列中,等待JS引擎线程执行。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrongu003E注:浏览器对同一域名的并发连接数是有限的,通常为 6 个。u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E宏任务u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E分为:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E同步任务:按照顺序执行,只有前一个任务完成后,才能执行后一个任务u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E异步任务:不直接执行,只有满足触发条件时,相关的线程将该异步任务推进任务队列中,等待JS引擎主线程上的任务执行完毕时才开始执行,例如异步Ajax、DOM事件,setTimeout等。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrongu003E微任务u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E微任务是ES6和Node环境下的,主要 API 有:u003Ccodeu003EPromiseu003Cu002Fcodeu003E,u003Ccodeu003Eprocess.nextTicku003Cu002Fcodeu003E。u003Cu002Fpu003Eu003Cpu003E微任务的执行在宏任务的同步任务之后,在异步任务之前。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRbZr3peD2vFXgS” img_width=”702″ img_height=”161″ alt=”在浏览器输入 URL 回车之后发生了什么?(超详细版)” inline=”0″u003Eu003Cpu003Eu003Cstrongu003E代码例子u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003Econsole.log(‘1’); u002Fu002F 宏任务 同步u003Cu002Fcodeu003Eu003Cbru003Eu003Ccodeu003EsetTimeout(function {u003Cu002Fcodeu003Eu003Ccodeu003E console.log(‘2’); u002Fu002F 宏任务 异步u003Cu002Fcodeu003Eu003Ccodeu003E})u003Cu002Fcodeu003Eu003Cbru003Eu003Ccodeu003Enew Promise(function(resolve) {u003Cu002Fcodeu003Eu003Ccodeu003E console.log(‘3’); u002Fu002F 宏任务 同步u003Cu002Fcodeu003Eu003Ccodeu003E resolve;u003Cu002Fcodeu003Eu003Ccodeu003E}).then(function {u003Cu002Fcodeu003Eu003Ccodeu003E console.log(‘4’) u002Fu002F 微任务u003Cu002Fcodeu003Eu003Ccodeu003E})u003Cu002Fcodeu003Eu003Cbru003Eu003Ccodeu003Econsole.log(‘5’) u002Fu002F 宏任务 同步u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E以上代码输出顺序为:1,3,5,4,2u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E参考文档u003Cu002Fh1u003Eu003Cpu003E[1] 你所不知道的 HSTS: http:u002Fu002Ft.cnu002FAiR8pTqxu003Cu002Fpu003Eu003Cpu003E[2] 详见这篇文章: http:u002Fu002Ft.cnu002FAiR8pnECu003Cu002Fpu003Eu003Cpu003E[3] MIME: http:u002Fu002Ft.cnu002FAiR8prtmu003Cu002Fpu003Eu003Cpu003E[4] 语法规范: http:u002Fu002Ft.cnu002FAiR80GdOu003Cu002Fpu003Eu003Cpu003E[5] 这篇文章: http:u002Fu002Ft.cnu002FAiR80c1ku003Cu002Fpu003Eu003Cpu003E[6] what-happens-when-zh_CN: http:u002Fu002Ft.cnu002FAiR80xb5u003Cu002Fpu003Eu003Cpu003E[7] Tags to DOM:http:u002Fu002Ft.cnu002FAiR80djXu003Cu002Fpu003Eu003Cpu003E[8] 彻底理解浏览器的缓存机制: http:u002Fu002Ft.cnu002FAiR8Ovobu003Cu002Fpu003Eu003Cpu003E[9] 浏览器的工作原理:新式网络浏览器幕后揭秘: http:u002Fu002Ft.cnu002FAiR8Oz06u003Cu002Fpu003Eu003Cpu003E[10] 深入浅出浏览器渲染原理: http:u002Fu002Ft.cnu002FAiR8O4fOu003Cu002Fpu003Eu003Cpu003E[11] js引擎的执行过程(一):http:u002Fu002Ft.cnu002FAiR8Ot3su003Cu002Fpu003Eu003Cpu003E如果喜欢本篇文章,欢迎。u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003E关注u003Cu002Fiu003E订阅号「Web项目聚集地」,回复「进群」即可进入无广告技术交流。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003E”

原文始发于:在浏览器输入 URL 回车之后发生了什么?(超详细版)

主题测试文章,只做测试使用。发布者:℅傍ㄖ免沦陷dε鬼,转转请注明出处:http://www.cxybcw.com/18102.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code