1. URL解析
第一步是将URL
中的每一部分给解析出来,比如:
https://user:pass@xxx.com:80/index.html?name=xx&age=11#top
- 协议:说明使用的协议
https/http/ftp
等,这个例子中为https
,https
协议比http
其实是多了一层ssl或tls
- 登录信息(认证):
@
前面这一部分user:pass
,一般我们不用管 - 域名:服务器地址,
xxx.com
- 端口:范围
0-65535
,用来识别应用在服务器哪个进程开启,http
默认端口为80
,https
默认443
,ftp
默认21
,比如https://www.baidu.com/
,浏览器会默认访问443
端口
image.png - 请求资源的文件路径:
/index.html
- 查询字符串:
?
后的键值对,由&
分隔,name=xx&age=11
- 哈希值(也称锚点):服务器很少使用,一般是客户端页面之间使用,比如现在流行的单页应用的
router
,很多原理就是通过监听哈希值改变页面片段来实现的
url编码解析
针对URL
中一些不识别的字符,比如中文或特殊字符,我们往往需要进行编码解析,解析方式一般有两种:
- encodeURI和decodeURI
encodeURI
是对整个URL
地址进行编码解析,主要是用来解析空格和中文字符的,比如:
let url = "http://xxx.com/index? name=小花&&age=11&&from=http://www.baidu.com/"
// 对整个URL编码:处理空格和中文
console.log(encodeURI(url)) // http://xxx.com/index?%20name=%E5%B0%8F%E8%8A%B1&&age=11&&from=http://www.baidu.com/
- encodeURIComponent
encodeURIComponent
主要针对的是字符串参数部分的编码,可以用来处理一些特殊字符,比如:/
等,如果不处理,浏览器会将这个http://xxx.com/index? name=小花&&age=11&&from=http://www.baidu.com/
识别成两个url地址http://xxx.com/index
和http://www.baidu.com/
// 针对字符串参数部分的编码
url = `http://xxx.com/index? name=${encodeURIComponent('小花')}&&age=11&&from=${encodeURIComponent('http://www.baidu.com/')}`
console.log(url) // http://xxx.com/index? name=%E5%B0%8F%E8%8A%B1&&age=11&&from=http%3A%2F%2Fwww.baidu.com%2F
扩展:URL,URN,URI的区别
- URL:统一资源定位符,定义了获取资源的方式
http://
和资源存放地址xxx.com/index
- URN:统一资源名称,使用特定命名空间的名字标识资源,但不包括获取方式,比如:
xxx.com/index#top
- URI:统一资源标识符,
URL
和URN
都是URI
的子集,也就是https://user:pass@xxx.com:80/index.html?name=xx&age=11#top
是URI
但在日常中,我们可以笼统地认为URL
就是URI
2. 查找浏览器缓存
浏览器缓存分为强缓存和协商缓存两种,当发送一个请求前,会先查看浏览器中是否有缓存,如果有,则从缓存中获取资源,否则,才重新发送请求。
缓存位置一般为: 内存缓存(memory cache
)和硬盘缓存(disk cache
)
- 打开网页时:会首先从硬盘缓存
disk cache
中查看是否缓存命中,如果有则使用,否则发送网络请求 - 当普通刷新网页(
F5
)时:因为网页标签tab
未关闭,所以可以会优先使用内存缓存,如果没有才会去硬盘缓存中查看是否有缓存,如果都没有才会发送网络请求 - 当强制刷新网页(
ctrl + F5
)时:浏览器不使用缓存,因为发送的请求头中会带有cache-control: no-cache
,服务器直接返回200和最新内容
设置缓存的方式
浏览器缓存分为强缓存和协商缓存两种:
- 强缓存:
- expires:
http1.0
的特性,通过服务端设置响应头expires
缓存过期时间,在这个时间前客户端不再发送请求,而是直接使用缓存; 缺点是这个过期时间是服务端根据服务端时间来设置的,而客户端浏览器则是通过客户端本地时间来判断是否过期的,如果本地时间错误,就会造成缓存失效; - cache-control:
http 1.1
的特性,是为了解决expires
存在的问题而出现的,这通过设置一个cache-control: max-age=2592000
,设置一个相对第一次获取资源后的存活时间2592000
(s),即30天后缓存失效,cache-control
的优先级高于expires
,cache-control
的值一般有以下这些:- max-age: 设置客户端缓存有效时间
- s-maxage:设置代理服务器缓存有效时间(仅在代理服务器中生效)
- no-cache:绕开客户端缓存,直接向服务器确认该资源是否过期(还可以使用协商缓存)
- no-store:不仅绕开客户端缓存,还不允许服务器缓存,即不使用任何缓存,只允许重新请求并下载资源
- public、private:是针对资源是否能被代理服务器缓存的一组对立设置,如果为
public
,即可被浏览器缓存,也可被代理服务器缓存;如果为private
,则只能被浏览器缓存,private
为默认值。
- 协商缓存
-
Last-Modified
http1.0
的特性,由服务器发送一个上次修改时间Last-Modified
标识给客户端,客户端下次发送请求时,将这个时间携带在请求头If-Modified-Since
上,由服务器比对两次时间,如果有修改,则重新发送更新后的资源,否则返回304
告知使用缓存。
缺点是:-
Last-Modified
的时间的最小间隔只能标识到1s
,如果服务器在1s
内修改了文件,那么客户端是无法拿到最新修改内容的。 - 修改了文件,但是后面我又修改回去,然后关闭文件,实际上相当于未修改文件,但由于修改时间变了,服务器会更新
Last-Modified
,导致客户端重新发请求。
以上两点,总结来说,就是服务器无法真正感知文件的变化。
-
ETag
http1.1
的特性,为了解决Last-Modified
存在的问题,服务器使用ETag
哈希值来识别文件是否被修改过,客户端再次请求时会携带ETag
的值到请求头If-None-Match
,服务器比对两次文件哈希值,如果有修改,则重新发送资源,否则返回304
使用缓存
请求页面时,会先检查有没有强缓存,如果没有强缓存或强缓存失效时,才会检查有没有协商缓存,如果协商缓存失效或不存在时,才会重新请求。
如何避免缓存
一般html
等静态资源文件我们是不做缓存的,这样是保证每一次html
都是一次新请求,可以通过以下方式来避免缓存:
- 服务器资源更新后,让资源名称和之前的不一样,使用
webpack
给打包后的文件名添加hash + name
值 - 每次服务器资源更新后,在导入
html
等静态资源时,设置一个后缀(时间戳、版本号或随机数)
<script src="index.js?145532685">
<script src="index.js?version='1.0.0'">
- 设置强缓存响应头:
Cache-Control: no-cache, no-store, must-revalidate
- 不使用强缓存,使用协商缓存,由服务器决定是否使用缓存
3. DNS解析
为什么要进行DNS解析?
一台主机的真正地址是IP,但IP是一些数字,不利用于人们记忆,所以就有了域名服务器,它是将域名和IP做了个映射关系,方便通过域名找到IP。
DNS解析过程,其实就是将域名解析到IP地址:
1.浏览器缓存 - 浏览器缓存DNS
记录一段时间
2.操作系统缓存 - 文件(比如Hosts
文件)查找是否有该域名和对应IP
3.路由器缓存 - 一般路由器也会缓存域名信息
4.ISP DNS
缓存 - 本地DNS服务器缓存去找,比如电信、移动等
5.都没有找到,则向根域名服务器查找域名对应IP
,根域名服务器把请求转发到下一级查找IP
www.baidu.com
查找顺序是: 根域名服务器(.)-> .com(顶级域名) -> .baidu.com(二级域名) -> www.baidu.com(三级)
,这里的查找顺序是通过本地DNS服务器
来承上启下的,每个域名服务器都要通过本地DNS服务器
来通信,如下图:
其中根域名服务器在域名地址中最后一个点,通常被省略,比如www.baidu.com
的完整域名应该是www.baidu.com.
其中1-5是递归查询,5操作则是迭代查询
扩展:DNS性能优化
每次DNS解析
都要花费20-120ms
,当存在多个不同的域名,这个花费时间会逐渐增长,想要优化它,我们第一想到的是减少使用不同的域名,但在实际项目中,我们往往不推荐这种,因为要考虑到一些服务拆分的其它因素:
- 资源的合理利用
- 抗压能力加强
- 提高HTTP并发数等
所以,服务器拆分是很有必要的,我们往往使用另一种方式:dns-prefetch
预获取的方式来优化DNS
解析,比如京东官网:
<link rel="dns-prefetch" href="//static.360buyimg.com">
4. 浏览器与服务器交互的过程
- 首先浏览器利用
tcp协议
通过三次握手
与服务器建立TCP
连接
http请求包括header和body。header中包括请求的方式(get和post)、请求的协议 (http、https、ftp)、请求的地址ip、缓存cookie。body中有请求的内容。 - 连接建立后,浏览器根据解析到的
IP
地址和端口号发起http
的get请求. - 服务器接收到
http
请求之后,开始搜索html页面,并使用http返回响应报文 - 若状态码为200显示响应成功,浏览器接收到返回的html页面之后,开始进行页面的渲染
5. 浏览器页面渲染过程
- 浏览器根据深度遍历的方式把
html
节点遍历成DOM树
- 将
css
解析成CSS DOM树
- 将
dom树
和CSS DOM树
构造成render树
- JS根据得到的
render树
计算所有节点在屏幕中的位置,进行布局(回流) - 遍历
render树
并调用硬件API
绘制所有节点(重绘)
参考:
https://www.jianshu.com/p/308212675adb
https://segmentfault.com/a/1190000014311983
https://www.cnblogs.com/qing-5/p/11126524.html