前言
作为前端工程师,几乎是每天都要与各种浏览器打交道。
理解浏览器是如何工作的,对我们做业务的技术选型、架构设计等都有非常重要的作用,可以让我们准确的评估web开的项目的可行性,站在更高维度审视页面,以及在快节奏的技术迭代中把握住问题的本质。
那么我们就从一道面试题在浏览器里面,从输入URL到展示页面,这过程发生了什么?
来解开浏览器神秘的面纱
在了解流程之前我们先来看一下浏览器的架构:
浏览器架构
当代现有的浏览器主要由:
- 用户界面(The user interface)、
- 浏览器引擎(The browser engine)、
- 呈现引擎(The rendering engine)、
- 网络(Networking)、
- JavasScript 解释器(JavaScript interpreter)、
- 用户界面后端(UI backend)、
- 数据存储组成(Data storage)。
- 用户界面 - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
- 浏览器引擎 - 在用户界面和呈现引擎之间传送指令。
- 渲染引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
- 网络 - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
- 用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
- JavaScript 解释器。用于解析和执行 JavaScript 代码。
- 数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。
那么在了解完浏览器的架构之后,再来看一下URL的解析过程
浏览器解析过程
- DNS解析
- TCP连接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染过程
DNS解析解析过程
当发送一个url请求时,不管这个url是web页面的url还是web页面上的每个资源的url,浏览器都会开启一个线程处理该请求,同时在远程DNS服务器上启动一个DNS查询,这能使浏览器获得请求对应的IP地址
- 本地DNS服务器拿到请求后会首先查询它的缓存记录,如果缓存有这条记录的话就直接返回。这个时候拿到的ip地址,会被标记为非权威服务器的应答。
- 如果没有,本地DNS服务器还要向DNS根服务器进行查询(会从配置文件里面读取13个根域名服务器的地址,然后向其中一台发起请求。)
- 根DNS服务器拿到这个请求后,告诉本地DNS服务器,你可以到域服务器上去继续查询,并给出域服务器的地址。这种过程是迭代的过程,(比如知道他是com.这个顶级域名下的,所以就会返回com域中的NS记录。)
- 然后本地DNS服务器继续向域服务器发出请求,在这个例子中,请求的对象是.com域服务器。.com域服务器收到请求之后,也不会直接返回域名和IP地址的对应关系,而是告诉本地DNS服务器,你的域名的解析服务器的地址,(比如,com域的服务器发现你这请求是baidu.com这个域的,一查发现了这个域的NS,那我就返回给你,你再去查。)
- 最后,本地DNS服务器向域名的解析服务器发出请求,这时就能收到一个域名和IP地址对应关系,本地DNS服务器把IP地址返回给用户电脑,并把这个对应关系保存在缓存中,以备下次别的用户查询时,可以直接返回结果,加快网络访问。(比如,向baidu.com这个域的权威服务器发起请求,baidu.com收到之后,查了下有www的这台主机,就把这个IP返回给你了,然后ISPDNS拿到了之后,将其返回给了客户端,并且把这个保存在高速缓存中。)
网址的解析是一个从右向左的过程: com -> google.com -> www.google.com;
事实上,真正的网址是www.google.com.,这个.对应的就是根域名服务器,默认情况下所有的网址的最后一位都是.,既然是默认情况下,为了方便用户,通常都会省略,浏览器在请求DNS的时候会自动加上,所有网址真正的解析过程为: . -> .com -> google.com. -> www.google.com.。
DNS域名类别
名称 | 说明 | 示例 |
---|---|---|
根域 | DNS域名使用时,规定由尾部局点(.)来指定名称位于根或者更高级的域层次结构 | 单个句点(.)或者句点用于末尾的名称 |
顶级域 | 用于指示某个国家/地区或者组织使用的名称类 | .com |
第二层域 | 个人或者组织在Internet上使用的注册名称 | baidu.com |
子域 | 已注册的二级域名派生的域名,同属的讲就是网站名 | www.baidu.com |
主机名 | 通常情况下,DNS域名的最左侧的标签标示网络上的特定计算机,如h1 | h1.www.baidu.com |
TCP建立连接
在http消息发送前,需要建立客户端与服务器的TCP链接,也就是进行所谓的三次握手。
TCP是因特网中的传输层协议,使用三次握手协议)建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK,并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接,TCP使用的流量控制协议是可变大小的滑动窗口协议。
TCP三次握手的过程如下:
- 客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
- 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
- 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
发起HTTP请求
进过TCP3次握手之后,浏览器发起了http的请求;
chrome浏览器查看报文首部信息:
服务器请求处理
现在请求只是成功达到了服务器,接下来服务器需要响应浏览器的请求。
服务器端收到请求后的由web服务器(准确说应该是http服务器)处理请求,诸如Apache、Ngnix、IIS等。web服务器解析用户请求,知道了需要调度哪些资源文件,再通过相应的这些资源文件处理用户请求和参数,并调用数据库信息,最后将结果通过web服务器返回给浏览器客户端。
在HTTP里,有请求就会有响应,哪怕是错误信息。这里我们同样看下响应报文的组成结构:
在响应结果中都会有个一个HTTP状态码,比如我们熟知的200、301、404、500等。通过这个状态码我们可以知道服务器端的处理是否正常,并能了解具体的错误。
状态码由3位数字和原因短语组成。根据首位数字,状态码可以分为五类:
状态码 | 类别 | 说明 |
---|---|---|
1-- | 信息性状态码 | 接收的请求正在处理 |
2-- | 成功状态码 | 请求正常处理完毕 |
3-- | 重定向状态码 | 需要进行附加操作已完成请求 |
4-- | 客户端错误状态码 | 服务器无法处理请求 |
5-- | 服务器错误状态码 | 服务器处理请求出错 |
从上面的URL请求我们能看到headers里面的Accept是text/html类型,这部分头部说明了浏览器将响应内容作为HTML渲染,而不是作为文件下载。浏览器将使用头部决定如何解释响应结果,当然也会考虑其他因素,比如URL的扩展情况。
浏览器渲染解析
当浏览器获得一个html文件时,会“自上而下”加载,并在加载过程中进行解析渲染。
解析:
- 浏览器会将HTML解析成一个DOM树,DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
- 将CSS解析成 CSS Rule Tree 。
- 根据DOM树和CSSOM来构造 Rendering Tree。注意:Rendering Tree 渲染树并不等同于 DOM 树,因为一些像 Header 或 display:none 的东西就没必要放在渲染树中了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。