从输入网址到页面渲染全过程

作为一名前端开发工程师,无论是在面试,还是在日常开发debug中,一定都会遇到一个常见的问题,当一个用户在浏览器输入网址并敲下回车键之后,直到页面完全渲染的这短时间里,到底发生了哪些事情?

DNS解析

DNS是一个把域名和ip地址相互映射的分布式数据库,这样的方式可以让用户能够以更加方便的方式去访问页面,当用户输入了想要访问的网址之后,浏览器便会对这个网址进行解析,以找到对应的服务IP地址。那么解析的过程及顺序是怎样的呢?

首先会找到浏览器缓存,本地的hosts文件DNS缓存。如果这些地方都没有找到对应的记录,则会去请求DNS服务器。而DNS服务又分为三步,首先会走到本地DNS服务,然后依次请求根域名服务器和顶级域名服务器,最终请求权威域名服务器的地址,权威域名服务器会对网址进行解析并返回最终的IP地址给到本地DNS服务器,由它传递到浏览器。此外,本地DNS服务器不仅要把IP地址返回给用户浏览器,还会把这个对应关系保存在缓存中,以备下次别的用户查询时,就可以直接返回结果,提升访问效率。

也就是:

根据上面阐述的过程,我们可以得知域名的DNS解析也是一个性能上的开销,在完成域名解析之前,浏览器不能从服务器加载到任何东西。那么有什么方法可以去尽量减少或者避免这个开销呢?

  • 尽可能的减少DNS请求次数
  • 代码层面上去做DNS预解析
js 复制代码
<link rel='dns-prefetch'href='baidu.com'>
  • 充分利用好CDN进行负载均衡。每个域名在经过DNS解析后会把域名的解析权交给cname()指向的内容分发(CDN)专用的DNS服务器。CDN专用的DNS服务器把CDN的全局负载均衡设备的ip地址返回给用户。最终提高用户的访问速度。

建立TCP连接

在获取准确的IP地址之后,浏览器会先建立TCP连接。TCP是一种面向连接的,可靠的,基于字节流的传输层通信协议。在建立连接的时候,我们需要至少三次握手: 主要的握手过程:

  • 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c),此时客户端处于 SYN_SENT 状态

  • 第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,为了确认客户端的 SYN,将客户端的 ISN+1作为ACK的值,此时服务器处于 SYN_RCVD 的状态

  • 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,值为服务器的ISN+1。此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接

为什么至少需要三次握手呢?

  • 第一次握手确认了客户端的发送数据能力

  • 第二次握手确认了服务端的接收数据能力

  • 第三次握手确认了服务端的发送数据能力和客户端的接收数据能力。

发起HTTP/HTTPS的请求

在建立完TCP连接之后就可以通过 HTTP 进行数据传输了。HTTP本身是一个明文传输的超文本传输协议,在传输的途中容易遭受到中间人攻击。所以,我们大多数的网站都会使用HTTPS的传输服务,会在 TCP 与 HTTP 之间多添加一层协议做加密及认证的服务。这里还会涉及到一个TLS的握手过程,主要是利用了对称加密和非对称加密相结合的方式,来保障数据传输的安全性。

主要的加密原理:

  1. 用户在浏览器发起HTTPS请求,默认使用服务端的443端口进行连接;
  2. HTTPS需要使用一套CA数字证书 ,证书内会附带一个公钥Pub ,而与之对应的私钥Private保留在服务端不公开;
  3. 服务端收到请求,返回配置好的包含公钥Pub的证书给客户端;
  4. 客户端收到证书,校验合法性,主要包括是否在有效期内、证书的域名与请求的域名是否匹配,上一级证书是否有效(递归判断,直到判断到系统内置或浏览器配置好的根证书),如果不通过,则显示HTTPS警告信息,如果通过则继续;
  5. 客户端生成一个用于对称加密的随机Key ,并用证书内的公钥Pub进行加密,发送给服务端;
  6. 服务端收到随机Key 的密文,使用与公钥Pub 配对的私钥Private 进行解密,得到客户端真正想发送的随机Key
  7. 服务端使用客户端发送过来的随机Key对要传输的HTTP数据进行对称加密,将密文返回客户端;
  8. 客户端使用随机Key对称解密密文,得到HTTP数据明文;
  9. 后续HTTPS请求使用之前交换好的随机Key进行对称加解密。

服务器响应数据

服务器收到了我们的请求之后,会根据相应的请求信息,把它的处理结果返回,也就是返回一个HTPP响应。

HTTP响应与HTTP请求相似,HTTP响应也由3个部分构成,分别是:

  • 状态行
  • 响应头(Response Header)
  • 响应正文

状态行:

状态行由协议版本、数字形式的状态代码、及相应的状态描述,各元素之间以空格分隔。

格式: HTTP-Version Status-Code Reason-Phrase CRLF例如: HTTP/1.1 200 OK

协议版本: 是用http1.0还是其他版本

状态描述: 状态描述给出了关于状态代码的简短的文字描述。比如状态代码为200时的描述为 ok

状态码: 状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值,如下:

1xx:信息性状态码,表示服务器已接收了客户端请求,客户端可继续发送请求。

  • 100 Continue
  • 101 Switching Protocols
  • 2xx:成功状态码,表示服务器已成功接收到请求并进行处理。

200 OK 表示客户端请求成功

  • 204 No Content 成功,但不返回任何实体的主体部分
  • 206 Partial Content 成功执行了一个范围(Range)请求

3xx:重定向状态码,表示服务器要求客户端重定向。

  • 301 Moved Permanently 永久性重定向,响应报文的Location首部应该有该资源的新URL
  • 302 Found 临时性重定向,响应报文的Location首部给出的URL用来临时定位资源
  • 303 See Other 请求的资源存在着另一个URI,客户端应使用GET方法定向获取请求的资源
  • 304 Not Modified 服务器内容没有更新,可以直接读取浏览器缓存
  • 307 Temporary Redirect 临时重定向。与302 Found含义一样。302禁止POST变换为GET,但实际使用时并不一定,307则更多浏览器可能会遵循这一标准,但也依赖于浏览器具体实现

4xx:客户端错误状态码,表示客户端的请求有非法内容。

  • 400 Bad Request 表示客户端请求有语法错误,不能被服务器所理解
  • 401 Unauthonzed 表示请求未经授权,该状态代码必须与 WWW-Authenticate 报头域一起使用
  • 403 Forbidden 表示服务器收到请求,但是拒绝提供服务,通常会在响应正文中给出不提供服务的原因
  • 404 Not Found 请求的资源不存在,例如,输入了错误的URL

5xx:服务器错误状态码,表示服务器未能正常处理客户端的请求而出现意外错误。

  • 500 Internel Server Error 表示服务器发生不可预期的错误,导致无法完成客户端的请求
  • 503 Service Unavailable 表示服务器当前不能够处理客户端的请求,在一段时间之后,服务器可能会恢复正常

响应头部:

由关键字/值对组成,每行一对,关键字和值用英文冒号":"分隔,典型的响应头有:

location:服务器通过这个头告诉浏览器跳到哪里。

server:服务器通过这个头告诉浏览器服务器的型号。

content-encoding:服务器通过这个头告诉浏览器数据的压缩格式。

content-length:服务器通过这个头告诉浏览器回送数据的长度。

content-language:服务器通过这个头告诉浏览器语言环境。

content-type:服务器通过这个头告诉浏览器回送数据的类型。

refresh:服务器通过这个头告诉浏览器定时刷新。

content-disposition:服务器通过这个头告诉浏览器以下载方式打开数据。

transfer-encoding:服务器通过这个头告诉浏览器数据是以分块方式回送的

以下三个表示服务器通过这个头告诉浏览器不要缓存

expires:-1

cache-control:no-cache

pragma:no-cache

响应正文

包含着我们需要的一些具体信息,比如cookie,html,image,后端返回的请求数据等等。

加载静态资源并渲染页面

在浏览器没有完整接受全部HTML文档时,它就已经开始显示这个页面了,浏览器是如何把页面呈现在屏幕上的呢?不同浏览器可能解析的过程不太一样,这里我们只介绍webkit的渲染过程,下图对应的就是WebKit渲染的过程,这个过程包括:

解析html以构建dom树 -> 构建render树 -> 布局render树 -> 绘制render树

浏览器在解析html文件时,会"自上而下"加载,并在加载过程中进行解析渲染。在解析过程中,如果遇到请求外部资源时,如图片、外链的CSS、iconfont等,请求过程是异步的,并不会影响html文档进行加载。

解析过程中,浏览器首先会解析HTML文件构建DOM树,然后解析CSS文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。这个过程比较复杂,涉及到两个概念: reflow(回流)和repain(重绘)。

CSS文件的下载和解析并不会阻塞DOM的解析,但是会阻塞Render树的渲染

DOM节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为relow;当盒模型的位置,大小以及其他属性,如颜色,字体,等确定下来之后,浏览器便开始绘制内容,这个过程称为repain。

页面在首次加载时必然会经历reflow和repain。reflow和repain过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少reflow和repain

当文档加载过程中遇到js文件,html文档会挂起渲染(加载解析渲染同步)的线程,不仅要等待文档中js文件加载完毕,还要等待解析执行完毕,才可以恢复html文档的渲染线程。因为JS有可能会修改DOM,最为经典的document.write,这意味着,在JS执行完成前,后续所有资源的下载可能是没有必要的,这是js阻塞后续资源下载的根本原因。所以我明平时的代码中,js是放在html文档末尾的。

JS的解析是由浏览器中的JS解析引擎完成的,比如谷歌的是V8。JS是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始。但是又存在某些任务比较耗时,如IO读写等,所以需要一种机制可以先执行排在后面的任务,这就是:同步任务(synchronous)和异步任务(asynchronous)。

JS的执行机制就可以看做是一个主线程加上一个任务队列(task queue) 。同步任务就是放在主线程上执行的任务,异步任务是放在任务队列中的任务。所有的同步任务在主线程上执行,形成一个执行栈;异步任务有了运行结果就会在任务队列中放置一个事件;脚本运行时先依次运行执行栈,然后会从任务队列里提取事件,运行任务队列中的任务,这个过程是不断重复的,所以又叫做事件循环(Event loop)。

断开TCP连接

现在的页面为了优化请求的耗时,默认都会开启持久连接(keep-alive),那么一个 TCP 连接确切关闭的时机,是这个 tab 标签页关闭的时候。这个关闭的过程就是四次挥手。关闭是一个全双工的过程,发包的顺序是不一定的。一般来说是客户端主动发起的关闭,过程如下图所示:

  • 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
  • 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
  • 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
  • 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

为什么交手只需要是三次,而挥手需要四次呢?

交手,是双端从关闭状态转向开启状态,这时候双端并不需要做什么准备。 而相反,结束的时候,双端都需要确认对方的数据已经全部传送完毕了,才能结束。 所以把释放连接的报文FIN和确认接收的报文ACK,分开发送。

相关推荐
勿语&21 分钟前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈23 分钟前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水1 小时前
简洁之道 - React Hook Form
前端
正小安3 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光5 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   5 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   5 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web5 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常5 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式