【开发问题】前端部署更新,页面不能及时更新

105 阅读4分钟

1.浏览器缓存的机制

浏览器缓存是指将资源保存在浏览器本地,以便下次访问相同资源时可以直接从本地获取,而不必重新下载和解析。

浏览器缓存的方式包括强缓存协商缓存两种:

  1. 强服务器:在一定时间内,直接从本地服务器中获取资源,而不必向服务器发送请求。可以通过设置HTTP响应头中的Expires和Cache-Control字段来控制是否服务器、服务器时间等。
  2. 协商服务器:在强服务器失效后,向服务器发送请求,服务端通过比较资源的Etag或者Last-Modified等字段来确定是否需要重新下载和解析资源。

2.前端构建工具的资源缓存

前端构建工具(如 Webpack、Rollup等),自动化具备构建、预留和优化前端资源功能。例如,在预留时使用 webpack,生成的 js/css 文件名就Hash包含Hash值。其原理依赖,只要文件内容不变,文件名就不会变,这样大大提高了静态文件存储的寿命。

打包更新,对使用其他资源文件(Hash.js.png等)是没问题的,但是客户端在访问页面时,浏览器加载 html 是不会带上所谓的哈希值或者版本号的。所以,还不能解决 html 文件的缓存问题。

3. 浏览器缓存优化方案

配置nginx代理配置

在服务端 nginx 中添加禁止缓存的 Header 配置配置。切记,,只对 html 设置禁止缓存,同时不允许 CDN 等对其缓存如下:

location / { 
    if ($request_filename ~* .*.(?:htm|html)$) {
        add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
    }
}

但是发现了一个问题:项目采用微前端技术搭建,很多项目资源都在同一域名下,只是路径不同。如果前面的方式直接在按照location中添加Header服务器控制配置,所有该域名下的html都将被禁止服务器。

为了避免这个问题,所以对路径进行了正则处理:

location ~ ^/static/(admin1|admin2|admin3)/ { 
    if ($request_filename ~* .*.(?:htm|html)$) {
        add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
    }
}

在上面的配置中,使用了以下响应头:

  • private只能被终端用户的浏览器缓存,不允许CDN等缓存。这个会影响到CDN缓存命中率,但在代码量较小的情况下,可以禁用。
  • no-cache需要进行协商服务器,发送请求到服务器确认是否使用服务器。或者可以使用max-age=0
  • no-store完全禁用服务器。
  • must-revalidateno-cache类似的,强制到服务端验证,适用于一些特殊场景,例如发送了校验请求,但发送失败之类。
  • proxy-revalidate与上面类似,要求代理服务器服务器验证有效。

配置CDN域名服务器

首先需要选择合适的CDN服务(如Cloudflare、腾讯云CDN等),创建CDN域名并将其绑定到网站上,配置CDN域名服务器规则,服务器规则决定哪些资源应该被服务器以及服务器服务器的时间。可以将服务器时间设置为几个小时或几天,或者取决于您网站的更新频率。

查看对应头信息中的 x-cache 字段。显示为MISS,说明未命中 CDN 缓存,是回源的。显示为HIT,说明未命中 CDN 缓存。

浏览器查看优化前后的页面

  • DOMContentLoaded: DOM 树构建完成。即 HTML 页面由上一级解析 HTML 结构到索引的封闭标签</html>
  • Load:页面加载完成。DOM树构建完成后,继续加载html/css中的img、icon等外部资源,加载完成后视为页面加载结束。
  • Finish:页面上所有http请求发送到响应完成的时间。http1.0/1.1协议规定,单个分配的请求并发量为个,即完成时6所有http请求发送到响应完成的时间为6个。

DOMContentLoaded 会比加载时间小,两者时间差大致等于 img、icon 等外部资源加载的时间。

完成的时间比 Load 大,意味着页面有相当部分的请求量。完成的时间比 Load 小,意味着页面请求量少,如果页面只有一个 html 文档请求的静态页面,完成时间基本等于 html 文档请求的时间。