最近項目作了一段時間了,爲了測試的準確性及節約更多的時間,咱們須要將前端中靜態資源的引用加上hash版本號,以方便測試在工做時不用總是去清理緩存,由於項目工程原本就使用了Gulp,因此就在gulpfile.js中加入了gulp-rev
和gulp-rev-collector
兩個插件來實現這個目的。css
最開始的時候還算順利,不過在測試的時候發現問題,執行任務命令後,只在第一次自動替換了html中的資源連接,而當咱們修改了源文件以後,就不行了,貌似找不到須要替換的關鍵字,因此有了下面的解決方案。html
咱們先來看看原來加上版本號是什麼樣的前端
"/css/style.css" => "/dist/css/style-1d87bebe.css" "/js/script1.js" => "/dist/script1-61e0be79.js" "cdn/image.gif" => "//cdn8.example.dot/img/image-35c3af8134.gif"
而如今咱們須要改爲這樣:node
"activity/channel/2.css": "activity/channel/2.css?v=4ddaaeae28" "activity/christmas.css": "activity/christmas.css?v=2d21a0c7ca" "activity/channel/1.jpg": "activity/channel/1.jpg?v=c8571d8112"
因此,咱們如今須要手動更改兩個插件的源碼:chrome
第一步:打開node_modules\gulp-rev\index.js 第144行npm
/*manifest[originalFile] = revisionedFile;*/ manifest[originalFile] = originalFile + '?v=' + file.revHash;
第二步:打開node_modules\rev-path\index.js 第10行json
/*return filename + '-' + hash + ext;*/ return filename + ext;
注意:這一步中,不少同窗找不到這個rev-path
,是由於之前的gulp-rev
插件將這部分集成在了裏面,然後續的版本將rev-path
從gulp-rev
裏抽離出來了,因此要在項目目錄的node_modules
裏找這個插件,固然,咱們不須要手動安裝,這是gulp-rev
的依賴,npm會自動安裝它。gulp
第三步:打開node_modules\gulp-rev-collector\index.js 第31行瀏覽器
/*if ( !_.isString(json[key]) || path.basename(json[key]).replace(new RegExp( opts.revSuffix ), '' ) !== path.basename(key) ) { isRev = 0; }*/ if ( !_.isString(json[key]) || path.basename(json[key]).split('?')[0] !== path.basename(key) ) { isRev = 0; }
第50行緩存
/*return pattern.replace(/[\-\[\]\{\}\(\)\*\+\?\.\^\$\|\/\\]/g, "\\$&");*/ var rp = pattern.replace(/[\-\[\]\{\}\(\)\*\+\?\.\^\$\|\/\\]/g, "\\$&"); rp = pattern + "(\\?v=(\\d|[a-z]){8,10})*"; return rp;
第90行
/*patterns.push( escPathPattern( (path.dirname(key) === '.' ? '' : closeDirBySep(path.dirname(key)) ) + path.basename(key, path.extname(key)) ) + opts.revSuffix + escPathPattern( path.extname(key) ) );*/ patterns.push( escPathPattern( (path.dirname(key) === '.' ? '' : closeDirBySep(path.dirname(key)) ) + path.basename(key, path.extname(key)) ) + opts.revSuffix + escPathPattern( path.extname(key) ) + "(\\?v=(\\d|[a-z]){8,10})*" );
OK,這樣就可使用gulpfile.js
裏定義的任務了,下面是個人gulpfile.js
/* 載入模塊 */ var gulp = require('gulp'), less = require('gulp-less'), mincss = require('gulp-minify-css'), concat = require('gulp-concat'), uglify = require('gulp-uglify'), clean = require('gulp-clean'), browerify = require('browserify'), sourcemaps = require('gulp-sourcemaps'), source = require('vinyl-source-stream'), buffer = require('vinyl-buffer'), replace = require('gulp-str-replace'), imagemin = require('gulp-imagemin'), browserSync = require('browser-sync'), rev = require('gulp-rev'), // 爲靜態資源文件替換帶MD5的文件名 revCollector = require('gulp-rev-collector'), // 替換靜態資源連接 runSequence = require('run-sequence'); // 順序執行 /* 自動刷新 start */ gulp.task('browser', function () { return browserSync({ port: 3000, open: true, startPath: '/', server: { directory: true, routes: { '/': '/' }, middleware: function (req, res, next) { console.log('middleWare.'); next(); }, baseDir: './' }, //指定瀏覽器 browser: 'chrome', //延遲刷新,默認爲0 reloadDelay: 1, //是否載入css修改,默認true injectChanges: true }); }); gulp.task('bro', function () { return gulp.src('./src/*') .pipe(browserSync.reload({ stream: true })); }); /* 自動刷新 end */ var fs = require('fs'); var fileContent = fs.readFileSync('./package.json'); var jsonObj = JSON.parse(fileContent); var argv = process.argv.pop(); var DEBUGGER = (argv === '-D' || argv === '-d') ? true : false; /* 基礎路徑 */ var paths = { css : 'src/common/css/', less : 'src/less/', scripts : "src/js/", img : "src/images/", html : "src/html/", build : "src/build/", src : 'src/' }; /* 項目資源文件目錄 */ var cssSrc = paths.src + 'css/block_css/*.css', jsSrc = paths.src + 'angular/modules/**/*.js'; if(DEBUGGER) { resProxy = "http://localhost:3000/build"; prefix = "http://localhost:3000/build"; } /* 清理css文件 */ gulp.task('clean-css', function () { return gulp.src([paths.build + "css/*.css", paths.src + "css/style.min.css"]) .pipe(clean()); }); /* 清理js文件 */ gulp.task('clean-js', function () { return gulp.src([paths.build + "js/*.js"]) .pipe(clean()); }); /* 編譯LESS */ gulp.task('runLess', ['clean-css'], function () { return gulp.src([paths.less + '**/*.less', paths.css + '**/*.css']) .pipe(less()) .pipe(concat('main.min.css')) .pipe(mincss()) .pipe(replace({ original : { resProxy : /\@{3}RESPREFIX\@{3}/g, prefix : /\@{3}PREFIX\@{3}/g }, target : { resProxy : resProxy, prefix : prefix } })) .pipe(gulp.dest(paths.build + "/css")) .pipe(browserSync.reload({stream:true})); }); /* 合併、壓縮CSS */ gulp.task('handleCss', ['clean-css'], function () { return gulp.src([paths.src + 'css/block_css/*.css']) .pipe(concat('style.min.css')) .pipe(mincss()) .pipe(gulp.dest('./src/css')) .pipe(rev()) .pipe(gulp.dest(paths.build + 'css')) .pipe(rev.manifest()) .pipe(gulp.dest('./rev/css')); }); /* 合併、壓縮 JS */ gulp.task('handleJs', ['clean-js'], function () { return gulp.src([paths.src + 'angular/app.js', paths.src + 'angular/modules/**/*.js']) .pipe(concat('app.min.js')) .pipe(uglify()) .pipe(rev()) .pipe(gulp.dest(paths.build + 'js')) .pipe(rev.manifest()) .pipe(gulp.dest('./rev/js')); }); /* CSS生成文件hash編碼並生成 rev-manifest.json文件名對照映射 */ // gulp.task('revCss', ['handleCss'], function(){ // return gulp.src('./src/css/style.min.css') // .pipe(rev()) // .pipe(gulp.dest(paths.src + 'build/css')) // .pipe(rev.manifest()) // .pipe(gulp.dest(paths.src + 'rev/css')); // }); /* js生成文件hash編碼並生成 rev-manifest.json文件名對照映射 */ // gulp.task('revJs', function(){ // return gulp.src(jsSrc) // .pipe(rev()) // .pipe(rev.manifest()) // .pipe(gulp.dest('rev/js')); // }); //Html替換css、js文件版本 gulp.task('revHtml', function () { return gulp.src(['./rev/**/*.json', './src/*.html']) .pipe(revCollector()) .pipe(gulp.dest('./src')); }); // 處理manage目錄中的連接替換 gulp.task('revManageHtml', function () { return gulp.src(['./rev/**/*.json', './src/manage/*.html']) .pipe(revCollector()) .pipe(gulp.dest('./src/manage')); }); /* 監聽HTML文件變化 */ gulp.task('html', function () { return gulp.src(paths.html + "**/*.html") .pipe(replace({ original : { resProxy : /\@{3}RESPREFIX\@{3}/g, prefix : /\@{3}PREFIX\@{3}/g }, target : { resProxy : resProxy, prefix : prefix } })) .pipe(gulp.dest(paths.build+'/html')) .pipe(reload({stream:true})); }); /* 壓縮圖片 */ gulp.task('images', function () { return gulp.src(paths.img + "**/*") .pipe(imagemin()) .pipe(gulp.dest(paths.build + "/images")); }); /* 解決js模塊化及依賴問題 */ gulp.task('browserify', function () { var b = browserify({ entries: ["./src/js/index.js"], debug: true }); return b.bundle() .pipe(source("index.js")) .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true})) .pipe(gulp.dest("./build/js")) .pipe(uglify()) .pipe(sourcemaps.write(".")) .pipe(replace({ original : { resProxy : /\@{3}RESPREFIX\@{3}/g, prefix : /\@{3}PREFIX\@{3}/g }, target : { resProxy : resProxy, prefix : prefix } })) .pipe(gulp.dest("./build/js")) .pipe(reload({stream:true})); }); /* Css樣式文件監控任務 */ gulp.task('watchCss', function () { gulp.watch('./src/css/block_css/*.css', function (done) { condition = false; runSequence(['handleCss'], ['revHtml'], ['bro'], done); }); }); /* 默認啓動任務 */ gulp.task('default', ['runLess', 'html', 'images', 'browserify'], function () { gulp.watch(['**/*.less', '**/*.css'], ['runLess']); gulp.watch('**/*.html', ['html']); gulp.watch('**/*.js', ['browserify']); }); /* 本地服務,自動刷新 */ gulp.task('server', function (done) { condition = false; runSequence(['browser'], ['handleCss'], ['handleJs'], ['revHtml'], ['revManageHtml'], ['bro'], done); gulp.watch('./src/css/block_css/*.css', function () { //監控全部CSS文件 runSequence(['handleCss'], ['revHtml'], ['revManageHtml'], ['bro'], done); }); gulp.watch(['./src/angular/**/*.js', './src/angular/*.js'], function () { //監控全部JS文件 runSequence(['handleJs'], ['revHtml'], ['revManageHtml'], ['bro'], done); }); gulp.watch([ './src/*.html', './src/views/*.html', './src/views/**/*.html', './src/manage/*.html'], ['bro']); });