浏览器路由
前言
路由这概念最开始是在后端出现的,在前后端不分离的时候,由后端来控制路由,服务器接收客户端的请求,解析对应的url路径,并返回对应的页面/资源。简单的说,路由就是根据不同的url地址来展示不同的内容或页面。
现在的网络应用程序越来越多的使用AJAX异步请求完成页面的无缝刷新,虽然Ajax解决了用户交互时体验的痛点,但是多页面之间的跳转一样会有不好的体验,所以便有了spa(single-page application)使用的诞生。而spa应用便是基于前端路由实现的,所以便有了前端路由。
前端路由的原理本质上就是在不刷新浏览器的情况下,请求下修改URL、检测URL的变化,截获URL的地址,通过解析、匹配路由规则从而实现UI的更新。路由的实现通常有两种形式一种是Hash模式,一种是History模式。
hash
vue-router 在实现单页面路由时,提供了两种方式:Hash 模式和 History 模式;vue2是 根据 mode 参数来决定采用哪种方式,默认是 Hash 模式,手动设置为 History 模式 。
hash
模式是一种把前端路由的路径用井号 #
拼接在真实 url
后面的模式。当井号 #
后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 onhashchange
事件。
hash 模式是利用了 window 可以监听 onhashchange 事件来实现的,也就是说 hash 值是用来指导浏览器动作的,对服务器没有影响,HTTP 请求中也不会包括 hash 值,同时每一次改变 hash 值,都会在浏览器的访问历史中增加一个记录,使用“后退”按钮,就可以回到上一个位置。所以,hash 模式 是根据 hash 值来发生改变,根据不同的值,渲染指定DOM位置的不同数据。
特点
- url中带一个 # 号
- 可以改变URL,但不会触发页面重新加载(hash的改变会记录在 window.hisotry 中)因此并不算是一次 HTTP 请求,所以这种模式不利于 SEO 优化
- 只能修改 # 后面的部分,因此只能跳转与当前 URL 同文档的 URL
- 只能通过字符串改变 URL
- 通过 window.onhashchange 监听 hash 的改变,借此实现无刷新跳转的功能。
- 每改变一次 hash ( window.location.hash),都会在浏览器的访问历史中增加一个记录。
- 路径中从 # 开始,后面的所有路径都叫做路由的
哈希值
并且哈希值它不会作为路径的一部分随着 http 请求,发给服务器 hash
永远不会提交到server
端(可以理解为只在前端自生自灭)。
history
window.history 属性指向 History 对象,它表示当前窗口的浏览历史。当发生改变时,只会改变页面的路径,不会刷新页面。 History 对象保存了当前窗口访问过的所有页面网址。通过 history.length 可以得出当前窗口浏览网址的记录。 由于安全原因,浏览器不允许脚本读取这些地址,但是允许在地址之间导航。 浏览器工具栏的“前进”和“后退”按钮,其实就是对 History 对象进行操作。
属性
- History.length:当前窗口访问过的网址数量(包括当前网页)
- History.state:History 堆栈最上层的状态值
主要API
- history.back():移动到上一个网址,等同于点击浏览器的后退键。对于第一个访问的网址,该方法无效果。
- history.forward():移动到下一个网址,等同于点击浏览器的前进键。对于最后一个访问的网址,该方法无效果。
- history.go(): 这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n),n可为正数可为负数; 默认参数为0,相当于刷新当前页面。
- History.pushState(satbleObject,title,URL):用于在历史中添加一条记录。pushState()方法不会触发页面刷新,只是导致 History 对象发生变化,地址栏会有变化。
- History.replaceState(satbleObject,title,URL):用来修改 History 对象的当前记录,用法与 pushState() 方法一样。
satbleObject 是一个JavaScript对象,与用pushState()方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,都会触发popstate事件,并能在事件中使用该对象。
popstate 事件:
每当 history 对象出现变化时,就会触发 popstate 事件。 window.addEventListener('popstate', e => {});
注意
- 仅仅调用pushState()方法或replaceState()方法 ,并不会触发该事件。
- 只有用户点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用History.back()、History.forward()、History.go()方法时才会触发。
- 该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。
- 页面第一次加载的时候,浏览器不会触发popstate事件。
缓存
在前端性能优化的方式中,最重要的当然是缓存了,使用好了缓存,对项目有很大的帮助。比如我们访问网页时,使用网页后退功能,会发现加载的非常快,体验感很好,这就是缓存的力量。
缓存有哪些好处?
- 缓解服务器压力,不用每次都去请求某些数据了。
- 提升性能,打开本地资源肯定会比请求服务器来的快。
- 减少带宽消耗,当我们使用缓存时,只会产生很小的网络消耗。
Web缓存种类: 数据库缓存,CDN缓存,代理服务器缓存,浏览器缓存。
浏览器缓存
每个浏览器都实现了HTTP缓存,我们通过浏览器使用HTTP协议与服务器交互的时候,浏览器会根据一套与服务器约定的规则进行缓存工作。浏览器的前进/后退,利用的是浏览器的缓存机制。
浏览器缓存其实就是指在本地使用的计算机中开辟一个内存区,同时也开辟一个硬盘区作为数据传输的缓冲区,然后用这个缓冲区来暂时保存用户以前访问过的信息。
浏览器缓存位置一般分为四类: Service Worker–>Memory Cache–>Disk Cache–>Push Cache。
强缓存
强缓存是当我们访问URL的时候,不会向服务器发送请求,直接从缓存中读取资源,但是会返回200的状态码。
我们第一次进入页面,请求服务器,然后服务器进行应答,浏览器会根据response Header来判断是否对资源进行缓存,如果响应头中expires、pragma或者cache-control字段,代表这是强缓存,浏览器就会把资源缓存在memory cache 或 disk cache中。
第二次请求时,浏览器判断请求参数,如果符合强缓存条件就直接返回状态码200,从本地缓存中拿数据。否则把响应参数存在request header请求头中,看是否符合协商缓存,符合则返回状态码304,不符合则服务器会返回全新资源。
以下是强缓存的字段
-
expires
是HTTP1.0控制网页缓存的字段,值为一个时间戳,服务器返回该请求结果缓存的到期时间,意思就是,再次发送请求时,如果未超过过期时间,直接使用该缓存,如果过期了则重新请求。
有个缺点,就是它判断是否过期是用本地时间来判断的,本地时间是可以自己修改的。
-
Cache-Control
是HTTP1.1中控制网页缓存的字段,当Cache-Control都存在时,Cache-Control优先级更高,主要取值为:
public:客户端和服务器都可以缓存(所有内容都将被缓存)。
privite:资源只有客户端可以缓存(Cache-Control的默认值)。
no-cache:客户端缓存资源,但是是否缓存需要经过协商缓存来验证。
no-store:不使用缓存(不使用强缓存,也不使用协商缓存)。
max-age:设置缓失效时间。
Cache-Control使用了max-age相对时间,解决了expires的问题。
-
pragma
这个是HTTP1.0中禁用网页缓存的字段,其取值为no-cache,和Cache-Control的no-cache效果一样。 http1.1中可用Cache-Control替换
缓存位置
强缓存会把资源房放到memory cache 和 disk cache中
- 存储图像和网页等资源主要缓存在disk cache
- 操作系统缓存文件等资源大部分都会缓存在memory cache中。
- 具体操作浏览器自动分配,看谁的资源利用率不高就分给谁。
查找浏览器缓存时会按顺序查找: Service Worker–>Memory Cache–>Disk Cache–>Push Cache。
1. Service Worker
是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
2. Memory Cache
内存中的缓存,主要包含的是当前页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。
2.Disk Cache
存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。
在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自 Disk Cache。
4.prefetch cache(预取缓存)
link标签上带了prefetch,再次加载会出现。
prefetch是预加载的一种方式,被标记为prefetch的资源,将会被浏览器在空闲时间加载。
5. Push Cache
Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
协商缓存
协商缓存就是强缓存失效后,浏览器携带缓存标识向服务器发送请求,由服务器根据缓存标识来决定是否使用缓存的过程。
主要有以下两种情况:
协商缓存生效,返回304
304 状态码表示客户端发送携带有缓存标识(GET方法的请求报文中包含If-Math,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since中任意首部)的请求时,因为服务器端的资源未改变,可直接使用客户端未过期的缓存。304状态码返回时,不包含任何响应的主体部分,304虽然划分到3XX类型状态码中,但是和重定向没有关系
协商缓存失效,返回200和请求结果
如果发起携带缓存标识资源请求时候服务器的资源已经更新。则返回请求结果,成功的话状态码为200
如何设置协商缓存
Last-Modified / If-Modified-Since
Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间。
If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件。
Etag / If-None-Match
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)。
If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200。
Etag / If-None-Match优先级高于Last-Modified / If-Modified-Since,同时存在则只有Etag / If-None-Match生效。
缓存方案
目前的项目大多使用的缓存方案:
- HTML:协商缓存
- css、js、图片:强缓存,文件名带上hash
强缓存与协商缓存的区别
-
强缓存不发请求到服务器,所以有时候资源更新了浏览器还不知道,但是协商缓存会发请求到服务器,所以资源是否更新,服务器肯定知道。
-
大部分web服务器都默认开启协商缓存。
刷新对于强缓存和协商缓存的影响
当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存。
当f5刷新网页时,跳过强缓存,但是会检查协商缓存。
浏览器地址栏中写入URL,回车 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快)
HTTP报文
HTTP请求报文
由请求行和HTTP头组成
请求行
包含请求的方法,请求的URI和HTTP版本3个字段组成
http协议得请求方法有:
- GET 获取资源
- POST 向服务器端发送数据,传输实体主体
- PUT 传输文件
- HEAD 获取报文首部
- DELETE 删除文件
- OPTIONS 询问支持的方法
- TRACE 追踪路径
HTTP请求头
- 包括通用信息头、请求头、实体头、空行
- 通用信息头指的是请求和响应报文都支持的头域,分别为Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via;
- 实体头则是实体信息的实体头域,分别为Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header。
- User-Agent:产生请求的浏览器类型。
- Accept:客户端可识别的内容类型列表。
- Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。
- 最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。
HTTP响应报文
HTTP响应也由四个部分组成,分别是:响应行
、响应头
、空行
、响应体
。
状态行
包含响应状态码,原因短语和HTTP协议版本
HTTP头
包括通用信息头、请求头、实体头
响应报文主体
响应状态码
状态码 | 含义 |
---|---|
1xx | 指示信息–表示请求已接收,继续处理 |
200 | OK 客户端发过来的数据被正常处理 |
204 | Not Content 正常响应,没有实体 |
206 | Partial Content范围请求,返回部分数据,响应报文中由Content-Range指定实体内容 |
301 | Moved Permanently 永久重定向 |
302 | Found 临时重定向,规范要求方法名不变,但是都会改变 |
303 | See Other 和302类似,但必须用GET方法 |
304 | 304 |
307 | Temporary Redirect 临时重定向,不该改变请求方法 |
400 | Bad Request 请求报文语法错误 |
401 | unauthorized 需要认证 |
403 | Forbidden 服务器拒绝访问对应的资源 |
404 | Not Found 服务器上无法找到资源 |
500 | Internal Server Error 服务器故障 |
503 | Service Unavailable 服务器处于超负载或正在停机维护 |
通用首部字段
首部字段名 | 说明 |
---|---|
Cache-Control | 控制缓存行为 |
Connection | 链接的管理 |
Date | 报文日期 |
Pragma | 报文指令 |
Trailer | 报文尾部的首部 |
Trasfer-Encoding | 指定报文主体的传输编码方式 |
Upgrade | 升级为其他协议 |
Via | 代理服务器信息 |
Warning | 错误通知 |
请求首部字段
首部字段名 | 说明 |
---|---|
Accept | 用户代理可处理的媒体类型 |
Accept-Charset | 优先的字符集 |
Accept-Encoding | Accept-Encoding |
Accept-Langulage | 优先的语言 |
Authorization | Web认证信息 |
Expect | 期待服务器的特定行为 |
From | 用户的电子邮箱地址 |
Host | 请求资源所在的服务器 |
If-Match | 比较实体标记 |
If-Modified-Since | 比较资源的更新时间 |
If-None-Match | 比较实体标记 |
If-Range | 资源未更新时发送实体Byte的范围请求 |
If-Unmodified-Since | 比较资源的更新时间(和If-Modified-Since相反) |
Max-Forwards | 最大传输跳数 |
Proxy-Authorization | 代理服务器需要客户端认证 |
Range | 实体字节范围请求 |
Referer | 请求中的URI的原始获取方 |
TE | 传输编码的优先级 |
User-Agent | HTTP客户端程序的信息 |