背景
单页面应用
现代前端框架大多数走的是单页面应用
路线,vue项目即是。对于这类应用,浏览器加载的内容主要为:
- 入口html文件:index.html
- 资源文件:chunk-vendors.*
浏览器缓存机制
浏览器缓存机制会使浏览器缓存许多类型的HTTP请求,包括.html
、.js
、.css
等
浏览器缓存机制的目的在于减少网络请求,提高网页加载速度,优化用户体验。也许有些时候它很讨厌:
有新更新的时候
但有的时候它很可爱:
当用户加载页面需要长达5s甚至更长的时间时
相比起可爱之处,它的讨厌之处是可以通过一些方法化解的。这就是接下来的内容。
缓存问题的解决之道
综上所述,要解决缓存带来的困扰,我们需要:
-
使得客户端及时更新
index.html
,只有这样才能够有机会更新.js
、.css
文件。以下是一个模拟的index.html
资源,用于理解为什么"只有这样"xml<html> <head> /*省略其他项*/ <link href="xx/xx/chunk-vendors.css"></link> </head> <body> <script type="text/javascript" src="xx/xx/chunk-vendors.js"></script> </body> </html>
单页面应用中的资源文件
.js
、.css
是要通过index.html
加载的。但是在完成版本更新之后,我们大多数时候已经移除了上一版本的js包,可是客户端仍能顺利完成加载,这是因为,连资源文件也一并缓存了。 -
更新资源文件,这里应该被分为两个部分:客户端请求新的资源文件(依赖index.html的更新),服务端提供新的资源文件。这里可以通过添加hash命名的方式使得新的
index.html
中使用新的资源包名,打包出来的资源文件也是对应的新包名xml<html> <head> /*省略其他项*/ <link href="xx/xx/chunk-vendors.78ushgag7y.css"></link> </head> <body> <script type="text/javascript" src="xx/xx/chunk-vendors.fgwjkrhakg23.js"></script> </body> </html>
中间的乱码是模拟的hash值
1.入口文件index.html
这里采用协商缓存机制解决index.html
的缓存
step-1.服务端添加响应头
arduino
Cache-Control: 'private no-cache'

其中:
private
: 仅允许响应被发送请求的客户端缓存(排除可能的中间代理的缓存)no-cache
: 客户端缓存内容,但是否使用缓存需要经过协商缓存决定
这个需要在nginx等服务器上匹配index.html
的请求路径,添加响应头实现
这里需要说明,仅仅在页面上添加<meta http-equiv="Cache-Control" content="no-cache" />
是不够的,因为浏览器在处理缓存时,依赖于 HTTP 响应头中的 Cache-Control
,所以这部分应该由服务端进行设置
step-2.协商缓存
1-首次请求资源时,服务器会在响应头中包含ETag
或 Last-Modified
Nginx默认会为静态文件生成
ETag
其他服务器应该也有类似实现

2-客户端再次请求时,在请求头中加入If-None-Match
orIf-Modified-since
If-None-Match: 上次响应头中的Etag
或
If-Modified-since: 上次响应头中的Last-Modified

3-服务器验证
有两对头信息可以用于协商缓存。
If-None-Match
&ETag
If-Modified-since
&Last-Modified
服务器比较请求头If-None-Match
与当前的ETag
,If-Modified-since
与当前的Last-Modified
是否匹配。
- 匹配:触发
304 Not Modified
响应告诉浏览器使用缓存的版本 - 不匹配:返回新的响应(新的静态页面、
ETag
、Last-Modified
)
2.资源文件
应用打包时配置hash打包即可,这样打包之后的资源文件就会带上hash值避免重复。
由于不同的打包工具、不同的插件应用有不同的写法,这一步可以自由尝试。
总结
通过以上方式,可以有效解决浏览器缓存问题,包括微信小程序的webview缓存。目前已测试较新的IOS机器和Android机器。
同时注意到:
协商缓存机制主要依赖于HTTP协议而不是浏览器特性,以上提到的HTTP头在HTTP/1.1规范中定义,这就意味着只要浏览器支持HTTP/1.1或更高版本协议,就可以支持协商缓存机制。
具体的行为也会受到浏览器设置、用户行为、网络条件的影响,因此更重要的是参考实际情况。比如在浏览器中用户手动进行了缓存设置的情况。