在浏览器输入 URL 回车后,会发生什么?
脚本之家
你与百万开发者在一起
这个问题已经是老生常谈了,更是经常被作为面试的压轴题出现,网上也有很多文章,但最近闲的无聊,然后就自己做了一篇笔记,感觉比之前理解更透彻了。
注意:本文的步骤是建立在,请求的是一个简单的 HTTP 请求,没有 HTTPS、HTTP2、最简单的 DNS、没有代理、并且服务器没有任何问题的基础上,尽管这是不切实际的。
大致流程
URL 解析
DNS 查询
TCP 连接
处理请求
接受响应
渲染页面
一、URL 解析
地址解析:
首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。
HSTS
由于安全隐患,会使用 HSTS 强制客户端使用 HTTPS 访问页面
其他操作
浏览器还会进行一些额外的操作,比如安全检查、访问限制(之前国产浏览器限制 996.icu)。
检查缓存
二、DNS 查询
基本步骤
1. 浏览器缓存
浏览器会先检查是否在缓存中,没有则调用系统库函数进行查询。
2. 操作系统缓存
操作系统也有自己的 DNS缓存,但在这之前,会向检查域名是否存在本地的 Hosts 文件里,没有则向 DNS 服务器发送查询请求。
3. 路由器缓存
路由器也有自己的缓存。
4. ISP DNS 缓存
ISP DNS 就是在客户端电脑上设置的首选 DNS 服务器,它们在大多数情况下都会有缓存。
根域名服务器查询
在前面所有步骤没有缓存的情况下,本地 DNS 服务器会将请求转发到互联网上的根域,下面这个图很好的诠释了整个流程:
需要注意的点
1.递归方式:一路查下去中间不返回,得到最终结果才返回信息(浏览器到本地DNS服务器的过程)
2.迭代方式,就是本地DNS服务器到根域名服务器查询的方式。
3.什么是 DNS 劫持
4.前端 dns-prefetch 优化
三、TCP 连接
TCP/IP 分为四层,在发送数据时,每层都要对数据进行封装:
1. 应用层:发送 HTTP 请求
在前面的步骤我们已经得到服务器的 IP 地址,浏览器会开始构造一个 HTTP 报文,其中包括:
请求报头(Request Header):请求方法、目标地址、遵循的协议等等
请求主体(其他参数)
其中需要注意的点:浏览器只能发送 GET、POST 方法,而打开网页使用的是 GET 方法
2. 传输层:TCP 传输报文
传输层会发起一条到达服务器的 TCP 连接,为了方便传输,会对数据进行分割(以报文段为单位),并标记编号,方便服务器接受时能够准确地还原报文信息。
在建立连接前,会先进行 TCP 三次握手。
3. 网络层:IP协议查询Mac地址
标头:数据包的发送者、接受者、数据类型
数据:数据包具体内容
验证是否配置虚拟主机 验证虚拟主机是否接受此方法 验证该用户可以使用该方法(根据 IP 地址、身份信息等)
301
永久重定向响应,浏览器就会根据响应,重新发送 HTTP 请求(重新执行上面的过程)。img
标签的src
属性,并将这个请求加到请求队列中。<
,>
状态都会产生变化。DOM
对象并把这些符号插入到DOM
对象中。<html>
<head>
<title>Web page parsing</title>
</head>
<body>
<div>
<h1>Web page parsing</h1>
<p>This is an example Web page.</p>
</div>
</body>
</html>
DOMContentLoaded
事件来通知DOM
解析完成。6.2. CSS 解析
div p { font-size :14px }
会先寻找所有的p
标签然后判断它的父元素是否为div
。display:none
的节点。auto
、百分比、px,比如把rem
转化为px
。specificity
的公式,这个公式会通过:标签名、class、id
是否内联样式
script
标签时,DOM 构建会被暂停,直至脚本完成执行,然后继续构建 DOM 树。CSS 会阻塞 JS 执行
JS 会阻塞后面的 DOM 解析
CSS 资源排在 JavaScript 资源前面
JS 放在 HTML 最底部,也就是
</body>
前
6.4. 布局与绘制
html
标签开始递归往下,重新计算位置和大小。display:none
会触发回流,而 visibility:hidden
只会触发重绘。1. 词法分析
分词,例如将
var a = 2
,,分成var
、a
、=
、2
这样的词法单元。解析,将词法单元转换成抽象语法树(AST)。
代码生成,将抽象语法树转换成机器指令。
2. 预编译
全局环境
函数环境
eval
创建变量对象
参数、函数、变量
建立作用域链
确认当前执行环境是否能访问变量
确定 This 指向
3. 执行
JS 引擎线程:也叫 JS 内核,负责解析执行 JS 脚本程序的主线程,例如 V8 引擎事件触发线程:属于浏览器内核线程,主要用于控制事件,例如鼠标、键盘等,当事件被触发时,就会把事件的处理函数推进事件队列,等待 JS 引擎线程执行定时器触发线程:主要控制 setInterval
和setTimeout
,用来计时,计时完毕后,则把定时器的处理函数推进事件队列中,等待 JS 引擎线程。HTTP 异步请求线程:通过XMLHttpRequest连接后,通过浏览器新开的一个线程,监控readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进事件队列中,等待JS引擎线程执行。
同步任务:按照顺序执行,只有前一个任务完成后,才能执行后一个任务
异步任务:不直接执行,只有满足触发条件时,相关的线程将该异步任务推进任务队列中,等待JS引擎主线程上的任务执行完毕时才开始执行,例如异步Ajax、DOM事件,setTimeout等。
Promise
,process.nextTick
。console.log('1'); // 宏任务 同步setTimeout(function() {console.log('2'); // 宏任务 异步})new Promise(function(resolve) {console.log('3'); // 宏任务 同步 resolve();}).then(function() {console.log('4') // 微任务})console.log('5') // 宏任务 同步
来源:https://4ark.me/post/b6c7c0a2.html
更多精彩
在公众号后台对话框输入以下关键词
查看更多优质内容!
女朋友 | 大数据 | 运维 | 书单 | 算法
大数据 | JavaScript | Python | 黑客
AI | 人工智能 | 5G | 区块链
机器学习 | 数学 | 送书