最近项目做了一段时间了,为了测试的准确性及节约更多的时间,我们需要将前端中静态资源的引用加上hash版本号,以方便测试在工作时不用老是去清理缓存,因为项目工程本来就使用了Gulp,所以就在gulpfile.js中加入了gulp-rev和gulp-rev-collector两个插件来实现这个目的。
最开始的时候还算顺利,不过在测试的时候发现问题,执行任务命令后,只在第一次自动替换了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"
而现在我们需要改成这样:
"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"
所以,我们现在需要手动更改两个插件的源码:
第一步:打开node_modules\gulp-rev\index.js 第144行
/*manifest[originalFile] = revisionedFile;*/
manifest[originalFile] = originalFile + '?v=' + file.revHash;
第二步:打开node_modules\rev-path\index.js 第10行
/*return filename + '-' + hash + ext;*/
return filename + ext;
注意:这一步中,很多同学找不到这个rev-path,是因为以前的gulp-rev插件将这部分集成在了里面,而后续的版本将rev-path从gulp-rev里抽离出来了,所以要在项目目录的node_modules里找这个插件,当然,我们不需要手动安装,这是gulp-rev的依赖,npm会自动安装它。
第三步:打开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']);
});