至於為什么要費盡心思地給文件添加指紋,請參看前端靜態資源緩存控制策略。這次要達到的小目標就是生成的資源文件能夠被客戶端緩存,而在文件內容變化后,能夠請求到最新的文件。
需要用到的 gulp 插件是 gulp-rev2 ,看清楚了,不是 gulp-rev + gulp-rev-collector 的蹩腳組合,而是 gulp-rev2。
gulp-rev2 的設計思路:
-
根據文件的內容
file.contents生成文件指紋(hash值); -
根據前面生成的文件指紋集合成一張
(源文件,構建文件)映射對照表(並保存在清單文件 rev-manifest.json 中); -
根據前面生成的映射對照表級聯更新存在引用的父文件;
下面以一個具體的栗子為例進行實踐。
栗子的目錄結構是這樣的:

其中包含一個 css 樣式源文件 .demo/demo.css,內容如下
a { background: url(../images/road.jpg); }
div { background: url(../images/a+b.jpg); }
接下來使用 gulp 進行構建,新建兩個任務,一個構建 image,一個構建 css,
const gulp = require('gulp');
const rev2 = require('gulp-rev2');
gulp.task('build:image', ()=>{
return gulp.src('./demo/**/*.{png,jpg,gif,ico}')
.pipe(rev2()) // 生成文件指紋並修改文件名
.pipe(gulp.dest('dist')) // 輸出到 dist 目錄
.pipe(rev2.manifest()) // 生成映射對照表 rev-manifest.js
.pipe(gulp.dest('.')); // 輸出到 gulpfile.js 同級目錄
});
gulp.task('build:css', ['build:image'], ()=>{
return gulp.src('./demo/**/*.css')
.pipe(rev2.update()) // 根據映射對照表更新存在引用的父文件
.pipe(gulp.dest('dist'))
});
然后執行 gulp build:css,會發現根目錄下生成了 dist 目錄,並在里面存放了構建后的文件,

細心的同學可能已經發現在構建之后 ./demo/image/ 目錄下的圖片資源已經添加了文件指紋,

這時候打開生成的 ./dist/demo.css,內容如下
a { background: url(../images/road-f7ee61d96b.jpg); }
div { background: url(../images/a+b-d41d8cd98f.jpg); }
可以看到引用了圖片資源的 css 文件內容也得到了相應更新。只要圖片資源發生更新(文件指紋就會發生變化),引用者(css文件)也應該級聯更新。這兩個應該是始終同步的,否則就會出現驢唇不對馬嘴引用錯亂。
文件指紋總算加上了,但是這種修改文件名的方式並不總是所希望的。有時我們可能需要更簡單傳統的方式,也就是通過url參數(query string)的形式進行關聯。也就是我們期望的 css 可能是這樣的:
a { background: url(../images/road.jpg?_v_=f7ee61d96b); }
div { background: url(../images/a+b.jpg?_v_=d41d8cd98f); }
gulp-rev2 已經提供了這個選項,可以說是服務非常周到。只需要在原來的 gulp 代碼上簡單加一行配置項:
. . .
.pipe(rev2({ // 生成文件指紋並修改文件名
query: true, // 以query string的方式進行指紋關聯
}))
. . .
重新執行 gulp build:css,打開 ./dist/demo.css,會發現樣式表已經變成了我們預期的樣子:
a { background: url(../images/road.jpg?_v_=f7ee61d96b); }
div { background: url(../images/a+b.jpg?_v_=d41d8cd98f); }
你可以嘗試對單個圖片資源進行更新(比如 override 操作),gulp-rev2 只會更新發生變動的資源的指紋,這也是所期望的。
注意點
...
.pipe(rev2.manifest()) // 生成映射對照表 rev-manifest.js
.pipe(gulp.dest('.')); // 輸出到 gulpfile.js 同級目錄
請確保 gulp-rev2 生成的清單文件 rev-manifest.js 輸出在 gulpfile.js 同級目錄下。這是因為 gulp-rev2 會在這個目錄進行讀取,否則將會導致錯誤。建議你在 .gitignore 文件中忽略掉該文件,因為它是構建生成的,沒有任何加入版本控制的需要。
