nginx 发布前端资源的缓存方案

前言

对于前端开发者来说,最头疼的事情莫过于。当你兴冲冲的给项目打包以后,上传到 nginx 等静态资源服务器发布以后,自己本地验证没问题,以为万事大吉,结果测试人员、用户反馈系统没更新,让你顿时感觉如同吃了苍蝇般难受。

没办法,作为一个专业的开发人员,用户反馈程序出问题,第一时间就要找到问题出现的原因。

于是你开始进行检查确认工作,先是打开浏览器访问系统发现没问题,确实是更新以后的版本。然后你开始对自己更新的过程产生了怀疑,上服务器,检查一下更新以后的资源文件,再三的详细比对,发现确实没问题后,你忽然恍然大悟,原来代码确实没问题,有问题的是浏览器缓存。

解决问题思路

前端打包后的文件,其实就是一些静态资源,我们一般情况下,是直接放在静态资源服务器提供服务的。

一般,解决发布资源缓存这个问题,有几种思路:

1. 浏览器设置每次都重新下载页面的所有的资源。

这个方案有个优点,每次拿到的资源都是重新拉去的最新的资源,所以不存在会缓存的问题。

但是这个方案的缺点非常明显,它直接让缓存失去了自身的意义。没有缓存,增加了服务器的负担,对服务器不太友好,同时也让用户使用网页的体验降低了。

而且,默认情况下,没有浏览器会这么设置。如果需要用这个方式,就需要引导用户这么设置,同时也对用户要求太高。

所以这个方案,不用考虑,直接就可以 pass 掉了。

2. 在静态资源服务器上配置,强制不让浏览器进行缓存。

常见的静态资源服务器,像 nginx、apache 等,确实可以配置让客户端永远不缓存静态资源。

虽然与第一个方案相比,它存在一些优点,比如不需要引导用户对浏览器进行配置。

但是弊病和第一种思路一样,对用户和服务器都极其不友好,所以这个方案也可以被 pass 掉了。

3. 最好的解决方案

其实有一种比较好的解决方案,需要前端和静态资源服务器配合起来达到。

前端资源其实分为几类:

  1. 内容会变化,但是文件名不会变,用户也只能通过某个路径访问到,最典型的就是页面入口的 index.html 文件
  2. 内容会变化,文件名也可以变化,比如每次打包生成的 js文件、css 文件、图片资源等
  3. 内容不会变化,文件名也不会变化,比如直接在 html 中引入的第三方包

针对这几种不同的解决方案,可以针对性的作优化。

第一类,可以采用 Etag 的缓存策略。

如果没了解过这个名词的童鞋,可以 mdn 上了解下相关知识:ETag - HTTP | MDN

简而言之,如果采取了 Etag 策略,那么每次请求某个资源的时候,服务器会在响的的请求头里返回 Etag 标签:

image

下次,如果再请求同样的资源的时候,浏览器就会在请求头里带上这个 Etag,让服务器进行对比,如果 Etag 能够匹配上,就返回 304,浏览器就明白,不用重新下载资源,直接用本地缓存就行;否则浏览器会返回200,并返回完整的请求资源。

image

比如,想要在 nginx 中配置 etag,可以参考下面的链接:

Module ngx_http_core_module

直接这样配置即可,同时要在 header 中加上 cache control 控制,让浏览器对 html 文件应用 no-cache 规则。

location ~ .*\.(?:htm|html)$ {
   ...
   add_header Cache-Control "no-cache";
   etag on;
   ...
}

不了解 cache-control 的童鞋,可以参考下 mdn 的文档:
Cache-Control - HTTP | MDN

这样配置了以后,对于所有的 html 文件,就达到了当文件没更新的时候,返回 304,当文件更新了,自动拉取最新的资源的目的。

第二类,直接采用强制浏览器缓存的方法。

对于第二类资源,我们可以通过添加 hash 的方式保证,即使前端版本更新了,文件名也不一样,那么我们就不需要像第一种方式一样,通过 Etag 方式去缓存了。

因为即使是 Etag 策略,其实也是会消耗请求资源的。对于这种发布了就不会更改的内容,直接可以通知浏览器长期缓存即可。

想达到这种效果,直接通过配置 cache control header 即可。

location ~ .*\.(?:js|css|jpg|jpeg|gif|png)$ {
   ...
   add_header Cache-Control "public, max-age=31536000";
   add_header Last-Modified "";
   etag off;
   ...
}

直接配置对于第二类资源,关闭 Etag,同时设置强制缓存,同时设置上一年的缓存期。

这样设置了以后,当浏览器再次请求该资源时候,会查看浏览器之前缓存的该资源是否还在有效期以内,如果在,便会直接应用该资源,不会向浏览器再次发起请求了;否则,会重新发起请求,请求该资源。

image

第三类资源的解决方案,其实跟第二类一样,通过添加 hash 值或者 timeStamp,用以区分资源,达到每次更新版本后,请求的资源不一致。

对于这两类资源的处理,在 nginx 上的配置,也并无差别,可以等同对待。

对于前端来说,就比较麻烦一些。

下面列举一些,在常用的前端框架的脚手架中,该如何配置,以达到自动添加 timeStamp 的效果吧。

  • create-react-app

在 react 这个官方脚手架中,支持从环境变量传值: Adding Custom Environment Variables | Create React App

所以,我们直接在模板中,替换调传入的值即可。

/** .env 文件 */
REACT_APP_WEBSITE_NAME=测试标题
REACT_APP_TIMESTAMP=1639814656911
/** html 模板文件,通过传值,替换掉模板中的内容 */
<title>%REACT_APP_WEBSITE_NAME%</title>
<script src="%PUBLIC_URL%/config.js?timeStamp=%REACT_APP_TIMESTAMP%"></script>
  • vue-cli

在 vue 脚手架中,可以通过重载 webpack 配置,来设置变量。

/**
 * @type {import('@vue/cli-service').ProjectOptions}
 */
module.exports = {
    chainWebpack: (config) => {
        config
            .plugin('html')
            .tap((args) => {
                args[0].timeStamp = Date.now();
                return args;
            });
    },
};

然后通过在 index.html 模板中,采用 lodash template 的方式,应用变量。

<script src="<%= BASE_URL %>config.js?timeStamp=<%= htmlWebpackPlugin.options.timeStamp %>"></script>

详细用法可以参考官方文档:HTML and Static Assets | Vue CLI

后记

其实对于同一个问题,往往会有多种不同的解决方案。有时候多研究一下,多思考一下,综合考量,就可以找出一种比较好的解决方案。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容