【前端面试】 输入网址后,浏览器发生了什么?

特别感谢

参考视频:

​ 客户端是如何请求服务端的

​ 浏览器是如何运作的

  本文是观看链接上的B站up主所总结的,再结合自身的理解,所写的一篇文字版整理,供自己以及各位看到的小伙伴在后续的学习中可以随时翻阅,文中会配上一些该视频上的图片来说明清楚某个概念,如果有不合适的地方请麻烦联系我删除,再一次感谢!

前言

  在前端面试中经常会碰到该问题:输入网址后,发生了什么?这里其实可以从两个方面作答,一方面是从网络方面来阐述当你输入网址后,是如何取到服务端数据的;另一方面是从浏览器方面的角度来进行作答,即当你输入网址后,浏览器做了什么事,让你可以把从服务端取到的数据给展示到页面上。本文主要从浏览器的角度出发来进行描述。

解析

1. 网络

  从网络方面的角度来看,以较为简单的流程来说,如下:

  1. [DNS解析] 输入网址后,DNS域名解析服务器进行解析。
  2. [寻址] 将解析得到的IP地址在网络中,向上级进行查找。
  3. [建立连接] 若能找到对应的地址,则与其建立连接;否则,向上层继续寻址,直到到NSP骨干网的路由器(拥有最大的路由表),查到地址后与其建立连接。
  4. [发起请求] 通过TCP的三次握手机制,建立连接后,将请求的数据包发给服务器;服务器接收到请求后,返回对应的资源给客户端。

通俗的例子

​  假设你需要与一位德高望重的老者通话,但你只知道他的名字(相当于域名),却不知道他的电话号码(相当于IP地址)

  1. [DNS解析] 你拨打了114,问客服说名字为这个的人,电话号码为多少?这时候客服查询之后,告诉了你他的电话号码。(名字 转换成 电话号码)
  2. [寻址] 坏的是,114没有告诉你这个电话号码的区号,你需要自己一个一个去找去试(寻址);如果实在找不到,你得到拥有最全的联系方式表的人那里去询问(即拥有最大路由表的NSP)那里去询问,完整的号码到底是多少?
  3. [建立连接] OK,当你得到完整的号码之后,这时候你就可以拨通电话那位德高望重的人,尝试与他建立起了连接。
  4. [发起请求] 当你拨通电话后,发现这位老者信号不太好,为了与他保持稳定的通话,你与他反复确认了彼此是否能听到彼此(TCP三次握手),直到你们确认完毕了(建立可靠连接后),就可以开始谈正事了。你与他交流的过程,也就是客户端与服务端进行请求与应答的过程。

2. 浏览器

  了解完网络方面,我们再来聊聊浏览器方面会发生什么。

1)首先需要先了解下浏览器中的进程

  早期旧的浏览器中,采用的是单进程,那它有什么样的缺点呢?

  1. [不安全] 进程内可以通过 IPC 进行通信,因此 JS 可以访问页面内其他进程,导致私密信息泄露
  2. [不稳定] 由于 JS 运行要依赖主线程,当代码阻塞了主线程时,一个标签页的卡死会导致整个浏览器卡死
  3. [不流畅] 一个进程负责太多事情,性能降低

  现代浏览器,采用的是多进程,优点在于:

  1. [性能快] 由于分开进程处理各个事务,性能比单进程会快得多
  2. [稳定] 每个新打开的标签页都是一个独立进程,一个标签页的卡死不影响其他标签页的使用
    【前端面试】 输入网址后,浏览器发生了什么?_第1张图片

通俗的例子

  [单进程] 一个人的精力有限,如果你同时既做前端、又做后端、运维、数据库,那么你在一定时间内的效率并不快(不流畅);而如果你在其中一个环节卡住思路,那么你的其他工作都会受影响而被阻塞(不稳定);由于你在作为程序员的同时,你又拥有数据库的最高权限,这时候你便可以动歪心思,来窃取数据库中用户的资料(不安全)。

  [多进程] 后来,你的老板多招聘了几个人,每个人分工明确,有人做前端,有人做后端,有人做数据库,并且都有单独的权限分配,各司其职。你们的工作可以并行开展,因此效率飞快(性能好);其中你们一个人如果请假了,也不影响其他人各自的工作(稳定性)。

2)了解完进程,我们再来看 浏览器 内部发生了什么。(下面的内容较难理解,可以打开链接中的视频,up主做了很详细的动画来帮助理解)

流程

  1. [捕捉地址] 输入地址后,浏览器进程的UI线程捕捉输入的地址

  2. [发送请求] 网络线程进行DNS解析,并将请求发送到服务端

  3. [接收数据] 服务端返回资源(以HTMLJavaScriptCSS为例)

  4. [渲染器进程] 浏览器进程将返回的资源通过 IPC 管道传给渲染器进程

  5. [DOM解析] 渲染器进程的主线程解析 HTML 文件,生成 DOM 节点树
    【前端面试】 输入网址后,浏览器发生了什么?_第2张图片

  6. [样式计算] 浏览器解析 CSS文件,进行样式计算,确定每个节点的样式

  7. [布局] 根据 DOM 节点树和计算好的样式,生成 LayoutTree,来确定每个节点放在页面上的哪个位置及大小

  8. [绘制] 遍历 LayoutTree 生成绘制记录表(Layer Tree),确定以何种顺序来绘制节点(如 z-index

  9. [分图层] 合成器线程按规则分图层,并分为更小的图块给栅格线程

  10. [栅格化] 将绘制记录表的信息转化成 draw quads 图块信息(记录了图块在内存中的位置、摆放顺序、页面位置等信息,用户可以直接看到的部分),并传回给合成器线程

  11. [合成] 合成器线程将图层进行组合,形成合成器帧,并传回给浏览器进程

  12. [渲染] 浏览器进程再将帧传回给 GPU 进行渲染展示
    【前端面试】 输入网址后,浏览器发生了什么?_第3张图片

注意

  • [JavaScript 会阻塞主线程] DOM 解析过程中遇到CSSImage 等资源不会阻塞 HTML 加载,因为其不会出现在 DOM 树节点中,而当遇到 script 标签时,会停止解析 DOM,原因在于:浏览器并不知道这段 JavaScript 是否会改变当前的 DOM 树,因此会执行完再继续解析 DOM,这就会引起页面渲染的卡顿。
  • [DOM和Layout树] DOM TreeLayout Tree 不一一对应。如设置了 display: none 的节点会出现在 DOM 树上,而不会出现在 Layout 树上;而设置了伪类如 ::after 的节点只会出现在 Layout 树上,而不会出现在DOM树。原因如下:
    • DOMHTML 解析获得,不关联样式
    • Layout 树是根据 DOM 和计算好的样式来生成

拓展

  1. [重绘] 改变节点的颜色属性时,触发了样式的重新计算,但不触发布局和绘制,称为重绘。

  2. [重排] 改变节点的位置属性信息时,会触发样式计算、布局、绘制及后面所有流程,称为重排。

  3. [占用主线程引起卡顿] 绘制、布局、JavaScript 都会占用主线程来执行,在每一帧的有限时间内,如果绘制和布局结束后还有多余的时间,则JavaScript 会利用空余时间来执行,如果 JavaScript 执行时间过长,不归还主线程,会导致下一帧渲染延迟,我们都知道,60帧是肉眼看起来比较不卡顿的帧率,而下一阵渲染延迟,也就是会引起画面卡顿。可以通过 requestAnimationFrame 的API来将 JavaScript 分割成更小的任务以不影响每一帧的运行,如下两图所示:
    【前端面试】 输入网址后,浏览器发生了什么?_第4张图片【前端面试】 输入网址后,浏览器发生了什么?_第5张图片

  4. [transform] 合成器线程和栅格化线程都不和 JS 抢占主线程。通过 transform 运行的动画只会在合成器和栅格化中执行,不会触发重绘和重排。

性能优化思路

  1. [避免重绘 & 重排] 尽量避免用 JavaScript 去改变 DOM 的样式、位置属性,操作量大时会引起页面卡顿。可以使用 transform 进行代替。
  2. [避免JavaScript阻塞主线程] 使用 requestAnimationFrame API分割 JavaScript 任务,或用异步调用的形式,来让 JavaScript不阻塞主线程。

你可能感兴趣的:(前端,面试,http,浏览器,html,面试)