浏览器导航渲染流程(从输入 URL 到页面展示,这中间发生了什么?)

转自---> 前端食堂

这是一道十分常见的面试题

image.png

流程

  • 1、用户在地址栏输入内容后,地址栏会将输入的内容进行合成 URL。

  • 2、当用户输入完内容并按下回车键时,浏览器会在当前页面执行 beforeunload 事件,你可以在这个钩子中询问是否要离开当前页面,常见于一些表单提交的场景。

  • 3、接下来开始导航流程,浏览器进入加载状态。

  • 4、浏览器的网络进程会先查找缓存中是否存在该资源,有的话直接返回,如果没有的话会发起 URL 请求。
    : 浏览器获取了这个url,当然就去解析了,它先去缓存当中看看有没有,从 浏览器缓存-系统缓存-路由器缓存 当中查看,如果有从缓存当中显示页面,然后没有那就进行步骤5;
    缓存就是把你之前访问的web资源,比如一些js,css,图片什么的保存在你本机的内存或者磁盘当中。
    在chrome浏览器中输入网址: chrome://chrome-urls/ chrome-urls是一个看到所有的Chrome支持的伪RUL,找到其中的chrome://appcache-internals/ 可以看见chrome的本地缓存地址:Instances in: C:\Users\User\AppData\Local\Google\Chrome\User Data\Default (0)

  • 5、接下来首先要进行的是 DNS 解析,获得请求域名的服务器的 IP 地址(这个过程我也画了一张图,放在下文),如果协议是 HTTPS,还需要建立 TLS 连接。

  • 6、接着利用目标服务器的 IP 地址建立 TCP 连接(三次握手),构建 HTTP 请求报文,发起请求。服务器收到请求后,会根据请求信息生成响应报文。

  • 7、浏览器的网络进程接收到响应报文后进行解析,如果状态码是 301 或者 302(还有 300、303 等 3xx 的状态码),则需要取得响应头中的 Location 对应的地址进行重定向,再重新发起请求。

  • 8、如果状态码是 200,浏览器会根据响应头中的 Content-Type 字段来识别返回的响应体数据类型,从而进行不同的流程。如 text/html 代表 html 格式, application/octet-stream 代表字节流类型,浏览器会按照下载类型来处理。

  • 9、如果是 HTML,浏览器会遵循 process-per-site-instance 默认策略准备渲染进程,准备好后就提交文档(将网络进程接收到的数据提交给渲染进程)。文档被提交后,渲染进程便开始进行页面解析和子资源的加载。

  • process-per-site-instance 默认策略:每个标签对应一个渲染进程,如果从一个页面打开了一个新页面,新打开的页面与当前页面还属于同一个站点的话,那么新页面会复用当前页面的渲染进程。

浏览器的网络进程会先查找缓存中是否存在该资源,有的话直接返回,如果没有的话会发起 URL 请求

image.png

目前 Chrome 的浏览器包括如下进程:

  • 1 个浏览器(Browser)主进程
  • 1 个 GPU 进程
  • 1 个网络(NetWork)进程
  • 多个渲染进程(运行在沙箱模式下)
  • 多个插件进程

HTML渲染流程

    在上图中一并画了出来,需要经过以下几个阶段:构建 DOM 树--->样式计算--->布局--->分层--->绘制--->分块--->光栅化--->合成

DNS

DNS 的解析是一个递归流程,顺序如下图中数字标记所示:


image.png
  • 根 DNS 服务器:返回顶级域 DNS 服务器的 IP 地址。
    扯一句:世界上有13台根服务器,而有N台根镜像服务器,所以有些国外地址会被墙 ~~~,说明域名解析第一步并不是走的根服务器,而是由根镜像服务器控制住做了过滤,有选择性地进行了域名解析,所以如果根域名服务器出了问题,我觉得我们国内仍然可以做到正常访问,当然只是新更新的域名无法访问。
  • 顶级 DNS 服务器:返回权威 DNS 服务器的 IP 地址
  • 权威 DNS 服务器:返回相应主机的 IP 地址

JS 栈 垃圾数据回收

  • 示例代码1:
function hello () {
    let name = '数据'
}
hello()
console.log(name) // Uncaught ReferenceError: name is not defined   在执行上下文时,存放在栈内存中的name地址和数据【内存块】已经回收掉了,所以在全局上下文中是访问不到。
  • 示例代码2:
function hello () {
    let name = '前端食堂'
    let food = { name: '回锅肉' } 
    function world () {
        var description = { slogan: '吃好每一顿饭' }
    }
    world()
}
hello()

上面的代码所对应的内存堆栈空间如下图所示:


image.png

栈中的垃圾回收比较简单,当一个函数执行结束后,JavaScript 引擎会通过向下移动 ESP 来销毁函数调用栈中所保存的执行上下文,ESP 就是记录当前执行状态的指针。

  • 垃圾回收操作会暂停 JavaScript 的运行,回收完毕后才会恢复执行,这种行为就是全停顿。

为了降低全停顿所带来的卡顿,V8 引擎采用了增量标记(Incremental Marking) 算法进行优化,将标记过程分为一个个小任务,这些小任务的执行时间比较短,可以穿插在其他的 JavaScript 任务中间执行,这样就不会有明显的卡顿了。

当然,V8 所采用的优化方案不只这一种,而是多种方案综合使用的,除了增量回收还有并行回收、并发回收等。

并行回收:垃圾回收器会使用多个辅助线程来并行执行垃圾回收
并发回收:回收线程在执行 JavaScript 的过程中,辅助线程在后台执行垃圾回收
如果你了解【** React 的 Concurrent 模式中时间切片的原理 **】,它的实现思想是不是与增量标记算法有异曲同工之妙呢。

你可能感兴趣的:(浏览器导航渲染流程(从输入 URL 到页面展示,这中间发生了什么?))