之前,我介绍了学习安装并配置前端自动化工具Gulp,觉得gulp确实比grunt的配置简单很多,于是我决定再深入学习一下gulp,就去网上查了资料,发现gulp还可以自动添加版本号,这个功能就为我平时在更新css或js时老是在客户端存在缓存导致更新后的效果无法实时展现的苦恼。所以就赶紧去试了一下,果真可以,很高兴啊,真是为项目开发,为效果的快速展现提供了很多的便利。
实现原理:
1、修改js和css文件;
2、通过对js,css文件内容进行hash运算,生成一个文件的唯一hash字符串(如果文件修改则hash号会发生变化);
3、替换html中的js,css文件名,生成一个带版本号的文件名。
现在网上的方案都是生成一个新的dist目录,里面包含了要发布的html、js、css等文件。但是在实际的公司的项目中,会有情况不能生成新的HTML进行发布,需要在原来的HTML文件上进行js 、css版本的替换. 这里分享下我在实际项目中通过改动插件然后在原目录结构下进行版本的控制方案。(在这里,我有点不太明白原作者的意思,因为你既然修改了js或css,那么html中引入这些文件的版本号必然会发生变化,也就是html也跟着变化了,如果你不对新的html进行发布,那线上的html中的版本号还是老的版本号,就没有起到更新缓存的作用,那我们辛辛苦苦的配置gulp来添加这个版本号干嘛?)
原html文件代码
预期效果:在原目录结构下html文件代码
<link rel="stylesheet" href="../css/default.css?v=5a636d79c4">
<script src="../js/app.js?v=3a0d844594"></script>
background:url("../images/none.png?v=8f204d4")
实现方法:
1、安装gulp和gulp插件
npm install --save-dev gulp
npm install --save-dev gulp-rev
npm install --save-dev gulp-rev-collector
npm install --save-dev gulp-asset-rev
npm install --save-dev run-sequence
2、编写gulpfile.js
//引入gulp和gulp插件
var gulp = require('gulp'),
assetRev = require('gulp-asset-rev'),
runSequence = require('run-sequence'),
rev = require('gulp-rev'),
revCollector = require('gulp-rev-collector');
//定义css、js源文件路径
var cssSrc = 'css/*.css',
jsSrc = 'js/*.js';
//为css中引入的图片/字体等添加hash编码
gulp.task('assetRev', function(){
return gulp.src(cssSrc) //该任务针对的文件
.pipe(assetRev()) //该任务调用的模块
.pipe(gulp.dest('src/css')); //编译后的路径
});
//CSS生成文件hash编码并生成 rev-manifest.json文件名对照映射
gulp.task('revCss', function(){
return gulp.src(cssSrc)
.pipe(rev())
.pipe(rev.manifest())
.pipe(gulp.dest('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', 'View/*.html'])
.pipe(revCollector())
.pipe(gulp.dest('View'));
});
//开发构建
gulp.task('default', function (done) {
condition = false;
runSequence( //需要说明的是,用gulp.run也可以实现以上所有任务的执行,只是gulp.run是最大限度的并行执行这些任务,而在添加版本号时需要串行执行(顺序执行)这些任务,故使用了runSequence.
['assetRev'],
['revCss'],
['revJs'],
['revHtml'],
done);
});
执行gulp命令后的效果
//rev目录下生成了manifest.json对应文件
{
"default.css": "default-803a7fe4ae.css"
}
<link rel="stylesheet" href="../css/default-803a7fe4ae.css">
<script src="../js/app-3a0d844594.js"></script>
很显然这不是我们需要的效果
3、更改gulp-rev和gulp-rev-collector
打开node_modules\gulp-rev\index.js
第144行 manifest[originalFile] = revisionedFile;
更新为: manifest[originalFile] = originalFile + '?v=' + file.revHash;
打开nodemodules\gulp-rev\nodemodules\rev-path\index.js
10行 return filename + '-' + hash + ext;
更新为: return filename + ext;
打开node_modules\gulp-rev-collector\index.js
31行 if ( !_.isString(json[key]) || path.basename(json[key]).replace(new RegExp( opts.revSuffix ), '' ) !== path.basename(key) ) {
更新为: if ( !_.isString(json[key]) || path.basename(json[key]).split('?')[0] !== path.basename(key) ) {
打开node_modules\gulp-assets-rev\index.js
78行 var verStr = (options.verConnecter || "-") + md5;
更新为:var verStr = (options.verConnecter || "") + md5;
80行 src = src.replace(verStr, '').replace(/(\.[^\.]+)$/, verStr + "$1");
更新为:src=src+"?v="+verStr;
再执行gulp命令,得到的结果如下(效果正确):
<link rel="stylesheet" href="../css/default.css?v=803a7fe4ae">
<script src="../js/app.js?v=3a0d844594"></script>
background:url("../images/none.png?v=8f204d4")
?但是假如我们更改了css和js后,再执行gulp命令,得到的结果会如下:
<link rel="stylesheet" href="../css/default.css?v=33379df310?v=803a7fe4ae">
<script src="../js/app.js?v=3a0d844594?v=3a0d844594"></script>
4、继续更改gulp-rev-collector有没有发现,会在版本号后面再添加一个版本号,因为gulp只替换了原来文件名,这样又不符合预期效果了,所以我们想到,还需要修改插件的替换正则表达式。
打开node_modules\gulp-rev-collector\index.js
第107行 regexp: new RegExp( '([\/\\\\\'"])' + pattern, 'g' ),
更新为: regexp: new RegExp( '([\/\\\\\'"])' + pattern+'(\\?v=\\w{10})?', 'g' ),
现在你不管执行多少遍gulp命令,得到的html效果都是
|
gulp.js文件
var gulp = require('guip')
, del = require('del')
, browserSync = require('browser-sync')
, uglify = require('gulp-uglify')
, csso = require('gulp-csso') //css压缩
, concat = require('gulp-concat') //文件合并
, clean = require('gulp-clean')//清空文件夹
, imagemin = require('gulp-imagemin') //
, rename = require('gulp-rename') //文件重命名
, rev = require('gulp-rev') //更改版本名
, revCollector = require('gulp-rev-collector')//gulp-rev的插件,用于HTML模板更改引用路径
, gulpsync = require('gulp-sync')(gulp); //异步处理
// 官网教程--------------
//删除文件
gulp.task('clean:mobile', function (cb) {
del([
'!dist/no/*',//取反模式
'dist/*',
], cb)
});
// 监视文件改动并重新载入
gulp.task('serve', function () {
browserSync({
server: {
baseDir: 'app'
}
});
gulp.watch(['*.html', 'styles/**/*.css', 'scripts/**/*.js'],
{cwd: 'app'}, browserSync.reload);//reload 也可以是其他的自定义任务
});
// 监视 Sass 文件的改动,如果发生变更,运行 'sass' 任务,并且重载文件
gulp.task('serve', ['sass'], function () {
browserSync({
server: {
baseDir: 'app'
}
});
gulp.watch('app/scss/*.scss', ['sass']);
});
// 同时输出压缩过的和未压缩版本的文件
gulp.task('rename', function () {
return gulp.src('foo.js')
// 这会输出一个未压缩过的版本
.pipe(gulp.dest('dist/'))
// 这会输出一个压缩过的并且重命名未 foo.min.js 的文件
.pipe(uglify())
.pipe(rename({extname: '.min.js'}))
.pipe(gulp.dest('/dist'));
});
// css压缩,添加版本号
gulp.task('rename', function () {
return gulp.src('*.css')
.pipe(csso())
// 此处不要修改文件名称,不然下面rev任务自动替换引用文件时会找不到要替换的内容
//在输出文件之前执行rev(),输出后的文件就会生成hash码
.pipe(rev())
.pipe(gulp.dest('/dist'))
.pipe(rev.manifest())//set hash key json
.pipe(gulp.dest('/dist/rev/'));
});
// rev需要单独执行才能为HTML引入的文件自动加上版本号
gulp.task('rev', function () {
return gulp.src(['rev/**/*.json', 'pages/**/*.html'])
.pipe(revCollector({
replaceReved: true
}))
.pipe(rename(function (path) {
// path.basename += '.min';
path.extname = '.html'
}))
.pipe(gulp.dest('/page-copy'));
});
//同步处理任务
gulp.task('default', gulpsync.sync(
[
'clean:mobile',
['less,js,css'],
'rev'
]
))