Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从浏览器输入域名 boblog.com 按回车后会发生什么事情? #1

Open
liangfengbo opened this issue Sep 25, 2019 · 0 comments

Comments

@liangfengbo
Copy link
Owner

commented Sep 25, 2019

从浏览器输入域名 boblog.com 按回车后会发生什么事情?

DNS解析

首先进行 DNS 解析,解析之前会进行 DNS 缓存查询,查询的步骤分为:

浏览器缓存 ——> 系统缓存 ——> hosts文件 ——> 路由器缓存 —— ISP(互联网服务提供商)DNS缓存

如果查询不到的话,客户端就会向 DNS 服务器发起 DNS 解析请求,DNS 解析分为2个步骤,分别是递归查询,和迭代查询:

  [递归查询]
   客户端    
        
   |    ^
   |    |
  查询 回复
   |    |
   v    |
                 —— 查询 ——>  DNS根服务器  
                 <—— 回复 ——  DNS根服务器  
             
 [迭代查询]       —— 查询 ——> .com服务器
本地DNS服务器     <—— 回复 —— .com服务器
             
                 —— 查询 ——> boblog.com服务器
                 <—— 回复 —— boblog.com服务器
  1. 本地DNS服务器问DNS根服务器:请问知道boblog.com的IP是多少吗?
  2. DNS根服务器:抱歉我也不知道,这个域名后缀是 .com,.com服务器应该知道,返回了.com域名服务器地址,你去找它吧

  1. 本地DNS服务器问 .com根服务器:请问知道boblog.com的IP是多少吗?
  2. .com根服务器:我也不知道,但我知道boblog.com服务器应该知道,返回了boblog.com的服务器地址

  1. 本地DNS服务器问 boblog.com的服务器:请问知道boblog.com的IP是多少吗?
  2. boblog.com的服务器:等等,我查询到了IP,且返回该IP地址

最后本地DNS服务器拿到IP后缓存起来且返回给客户端IP地址。DNS解析是一个很耗时的查询,可以进行设置DNS缓存和DNS预解析优化。

TCP/IP连接

拿到IP后就开始进行TCP连接,TCP连接需要经过三次握手才能确认连接,目的是确保数据能够安全到达送达。

三次握手大白话的经过:

  1. 客户端:朋友,你有快递到了,你现在方便签收快递吗?
  2. 服务端:收到,我现在在家,麻烦你现在送过来吧!
  3. 客户端:好的,马上给你送过去!

HTTP请求/响应

确认连接后,就开始利用TCP/IP协议族进行网络通信,客户端从应用层发起了一个HTTP请求,HTTP请求包含HTTP请求报文,包含请求头,请求体,比如请求方法有GET, POST方法,采取HTTP1.1协议。

为了方便传输,在传输层 TCP 协议把从应用层接收到的 HTTP 请求报文进行分割,并且在各个报文上打上标记序号和端口号转发给网络层。

在网络层IP协议,增加作为通信目的地的 MAC 地址后转发给链路层。这样客户端发送的请求接收,到了服务端后开始从链路层开始接收到数据,按顺序一层层发送到应用层,在应用层开始解析 HTTP 的请求头和请求体。

如果需要重定向,HTTP直接返回数据的状态码 301 或者 302,同时在请求头的 Location 字段附上需要重定向的地址,浏览器会根据 Location 进行重定向操作。

如果不需要重定向,服务器根据请求头中的 if-Modified-Since 和 If-None-Match 会验证资源是否需要更新,如果不用更新,返回 304 状态码,协商缓存,相对于告诉浏览器说缓存的资源还没有过期可以继续使用,就不返还新的数据了。如果过期了,则返回新的数据和200状态码,并且想浏览器想更新缓存数据的话,就在相应的头加上 Cache-Control: max-age=[秒],或 Last-modified 新的时间戳或 ETag 新的标识符,最后 HTTP 响应返回数据,当数据传输完成,TCP需要经过四次挥手断开连接。

四次挥手大白话:

  • 客户端:你好,我这边没有数据需要传输了,我们关闭连接吧!
  • 服务端:好的,我也查查我这边有没有数据需要传输。
  • 服务端:你好,我这边也没有数据需要传输了,我们关闭连接吧!
  • 客户端:好的,再见!

如果浏览器或者服务器头部加上 Connection: Keep-Alive,TCP就会一直保持连接,保持TCP连接可以省下下次连接建立的时间,提升资源加载的速度。从HTTP/1.1起,浏览器默认都开启了Keep-Alive,保持连接特点,但也有超时时间。

资源加载渲染

浏览器接收到数据包后进行解析,根据响应头中的 Content-type 来判断响应数据的类型,如果是字节流类型,就将该请求交给下载管理器,如果是text/html类型或者其他类型,就通知浏览器进程获取到文档准备渲染。

浏览器开始解析HTML,构建DOM树,解析CSS,生成CSS规则树,然后合并DOM树和CSS规则,生成render树,有了render树,接下来就是开始渲染绘制,包含计算CSS样式,各元素尺寸、位置的计算等等,浏览器会将各层的信息发送给GPU,GPU会将各层合成,显示在屏幕上。

如果遇到JS脚本,JS引擎首先会读取代码,进行词法分析,然后将代码分解成词元,对词元进行语法分析,将代码整理成语法树,使用翻译器将代码转为字节码,使用字节码解释器,将字节码转为机器码。

到了JS的执行阶段:

  • 执行上下文,执行堆栈概念(如全局上下文,当前活动上下文)
  • VO(变量对象)和AO(活动对象)
  • 作用域链
  • this机制等

执行上下文

执行上下文指当前的执行环境中的,变量、函数声明,参数,作用域链和this等信息。

包含2个上下文:

  1. 全局执行上下文(只有一个)
  2. 函数执行上下文(每次调用函数都会创建一个新的上下文)

包含的3个重要属性:

  1. 变量对象(VO)
  2. 作用域链(Scope Chain)
  3. this
const ExecutionContext = {
    VO: window, // 变量对象
    ScopeChain: {}, // 作用域链
    this: window
};

执行上下文的生命周期

执行上下文的生命周期分为2个:创建和执行。

  • 创建阶段:
    • 创建变量对象
    • 建立作用域链
    • 确定 this 的指向
  • 执行阶段:
    • 变量的赋值
    • 函数的引用
    • 执行其他的代码
  • 执行完毕后,从上下文栈顶弹出,等待回收

执行上下文栈

JS引擎首先会创建一个执行上下文栈,作用是管理上下文。在JS解释器初始化时首先创建一个全局上下文且压入上下文栈顶中,随着遇到函数调用时,会创建一个新的函数执行上下文且压入到上下文栈顶中,随着函数执行完后,会被上下文栈顶弹出,直回到全局上下文。

变量对象

变量对象指上下文中的数据作用域,储存着上下文的变量和函数声明。变量对象的工作也分为2种阶段:进入上下文和代码执行。

  • 进入上下文阶段:
    • 如果是函数上下文,会处理函数参数:如果没有传入,则初始化参数值为undefined
    • 函数声明:如果发生函数名字相同,则后者会覆盖前者
    • 变量声明:初始化变量为undefined,如果发生与其他变量或者函数声明同名,则会忽略
  • 代码执行阶段:
    • 按顺序执行,进行修改赋值

作用域

作用域指:变量与函数的可访问范围,控制着变量及函数的可见性与生命周期,分为:

  • 全局作用域:全局对象
  • 函数作用域:在函数定义的变量对象
  • 块级作用域:只在当前作用域内有效

作用域链

自由变量指当前作用域没有定义的变量,当查找自由变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。

var a = 100
function fn() {
    var b = 200
    
    function fn2() {
        var c = 300
        // 当前作用域没有定义的变量,即“自由变量”
        // 函数作用域可以访问全局作用域的变量
        console.log(a) // a 是自由变量
        console.log(b) // b 是自由变量
        console.log(c)
    }
    fn2()
    
}
fn()

this

this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用,分为以下5种情况:

  1. 默认绑定:在全局作用域下调用,this绑定的是全局window对象,如果在严格模式下,this绑定的是undefined
  2. 隐式绑定:如果调用的位置有上下文对象,则 this 指向的是这个上下文对象。如果有多个上下文嵌套,this 指向的是最内层的上下文对象。
  3. 显式绑定:如果调用函数使用了 call(),apply(),bind() 调用,this 指向的是绑定这个对象。
  4. new绑定:如果函数使用 new 调用,则 this 指向的是新创建出来的这个对象。
  5. ES6箭头函数是继承最外层函数调用的this绑定

this优先级

new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

垃圾回收机制

一些函数的生命周期结束后,从上下文栈弹出时会进行垃圾回收,JS垃圾回收机制常用分为2种:

  • 标记清除
  • 引用计数
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant
You can’t perform that action at this time.