老树新花:旧有项目的简易前端资源缓存处理

老旧的项目,日复一日的缝缝补补,没有大的动荡,其实一直维持着原有的样子也是能够的...html

不过就这样子的话也太没意思了。前端

眼看着当前各类炫酷的框架操做,好生羡慕,旧项目积重难返,但内心却早已种草《大公司里怎样开发和部署前端代码》,想动手实现一番。文章里的思路,在评论里有人指出 Ruby on Rails 其实早就有这样的实现了,即 Asset Pipeline,这两均可以拜读一番。node

趁着手上工做告一段落,暂时有空闲时间,跟后端小伙伴商量着怎么处理这个缓存问题,因每次发版都会引起缓存问题,运营人员、用户每次都要强刷,确实蛋疼。webpack

期间找了现有的方案,找来找去仍是百度出的 fis 知足一切需求,可是项目已经停滞了许久,便做罢。后面想着与其这样动找西找的,不如本身动手拼凑个!!!git

现有问题

  1. 项目是旧有的合做方式:前端出稿、脚本交互,PHP 后端套版,资源引用都是写死耦合在 PHP 模板内,不可能抽离(前面咨询事后端的小伙伴,由于框架自己有提供缓存功能,可是由于本身定义了一套资源加载的方法因此自带的缓存管理就用不上了)。
  2. 前端有本身的静态资源发布权限,只是修改样式、脚本、图片时能够本身操做发布。可是问题是资源的版本管理是经过路径后附带查询字符串的形式去实现的,且由后端手动控制,这边修改一次须要后端手动修改版本号两边一块儿发版。
  3. 因这边的全部缓存管理都是经过附带同一个查询字符串实现,当修改一个时,其它的文件的版本号亦一同改变,全部缓存失效,须要从新来过,缓存形同虚设。
  4. 图片文件更新后,得不到有效更新,使用的仍是旧内容。
  5. 修改版本查询字符串还不必定生效(后面会说明为何会这样)

经过上述暴露的问题,就着手一个一个拆分解决了。github

针对第一个问题,后端经过修改原有的加载方式,在加载的时候都去读取由前端这边生成的资源表匹配资源,在表上的就替换上去,不在的就回退到备用(原有)的资源那边。web

能够看到关键词:资源表、匹配、替换,确定不多是人肉去搞这个了,这样神都救不了你。项目自己使用的 gulp 去作一些重复的操做的(压缩、混淆、复制搬运这些),那就继续在这上面捣鼓好了(webpack只是浅尝辄止)。chrome

资源表的生成

这个能够经过 gulp-revgulp-rev-collector 插件来生成、替换内容,因学艺不精因此里面有些是人肉写的,未经过插件去实现。npm

生成文件指纹:gulp

  • 建立时间(有隐患:本地 git 删除,从新拉取,其建立时间、修改时间、访问时间都是同样的)
  • 文件内容

借助于 nodecrypto 模块,咱们能够经过如下方式简单的了解下指纹的生成(代码来自 npm 包插件:rev-hash):

'use strict';
const crypto = require('crypto');

module.exports = input => {
    if (typeof input !== 'string' && !Buffer.isBuffer(input)) {
        throw new TypeError('Expected a Buffer or string');
    }

    return crypto.createHash('md5').update(input).digest('hex').slice(0, 10);
};

喏,这就是指纹生成啦。

指纹的生成简单,可是替换上就比较麻烦了,由于路径都是写在后端模板内的,你前端也操做不了。因此这里就须要生成资源表给到后端,后端经过读取该资源表去匹配替换了。

后端处理好该问题,那么上述的第2、第3、第四的问题也就解决了。后端发版时,拉取新的资源表便可,无需再去手动修改版本号,前端能够更好的控制资源了。

由于项目中用到了 requirejs,因此其配置文件也须要替换,没有用到 gulp-rev-collector,人肉简单的使用了 replace 方法去替换资源表上的内容。

旧资源与新资源

期间就遇到这样的问题:每次更新资源时,删除整个版本目录,生成新的版本资源仍是保留原有资源,生成新的资源。

整个删除(覆盖式发布):在资源发版后会有短暂的资源找不到的问题(有可能来不及刷新资源表),可是好处是不会有冗余的文件存在。

保留旧文件,生成新文件(非覆盖式发布):随着时间的推移,版本化的文件内容会日积月累,变得越发庞大,单是咱们这边设想的起码保留3+1份(上上次、上次、当前 + 回退用的未版本化的文件)。

固然,为了不前者带来的短暂问题,咱们选择了后者,经过定时任务去清理过时文件(git 从新拉取到本地的话,全部文件的建立时间、修改时间、访问时间都是同样的,因此不能在本地对其进行操做)。

查询字符串仍是重命名文件

上面有提到过该问题,咱们采用的重命名文件的形式,为何不使用附带查询字符串的形式呢?

其实前面的知乎大神已解释了该缘由,《高性能网站建设指南》的做者也在书中提到了,这是他博文上的观点:Revving Filenames: don’t use querystring,博文是10年前的了...10年前的...10年前...10年...10...1...。

非覆盖式发布的资源冗余处理

200 vs 304

经过查看 chromenetwork 面板,能够看到 size栏显示了第二次访问的资源,有些显示的是 from memory cache,有些则是 from disk cache,status栏显示的状态码都是 200

而在 firefox 下显示的倒是 304 已缓存的状态,这又是为何呢?

相关文章
相关标签/搜索