以前,我介绍了学习安装并配置前端自动化工具Gulp,以为gulp确实比grunt的配置简单不少,因而我决定再深刻学习一下gulp,就去网上查了资料,发现gulp还能够自动添加版本号,这个功能就为我平时在更新css或js时总是在客户端存在缓存致使更新后的效果没法实时展示的苦恼。因此就赶忙去试了一下,果然能够,很高兴啊,真是为项目开发,为效果的快速展示提供了不少的便利。javascript
实现原理:css
一、修改js和css文件;html
二、经过对js,css文件内容进行hash运算,生成一个文件的惟一hash字符串(若是文件修改则hash号会发生变化);前端
三、替换html中的js,css文件名,生成一个带版本号的文件名。java
如今网上的方案都是生成一个新的dist目录,里面包含了要发布的html、js、css等文件。可是在实际的公司的项目中,会有状况不能生成新的HTML进行发布,须要在原来的HTML文件上进行js 、css版本的替换. 这里分享下我在实际项目中经过改动插件而后在原目录结构下进行版本的控制方案。(在这里,我有点不太明白原做者的意思,由于你既然修改了js或css,那么html中引入这些文件的版本号必然会发生变化,也就是html也跟着变化了,若是你不对新的html进行发布,那线上的html中的版本号仍是老的版本号,就没有起到更新缓存的做用,那咱们辛辛苦苦的配置gulp来添加这个版本号干吗?)node
原html文件代码正则表达式
预期效果:在原目录结构下html文件代码npm
<link rel="stylesheet" href="../css/default.css?v=5a636d79c4"> <script src="../js/app.js?v=3a0d844594"></script> background:url("../images/none.png?v=8f204d4")
实现方法:json
一、安装gulp和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
二、编写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>
很显然这不是咱们须要的效果
三、更改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>
四、继续更改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效果都是
2 <link rel="stylesheet" href="../css/default.css?v=5a636d79c4"> <script src="../js/app.js?v=3a0d844594"></script> 如下是本人本身写的一个既能够编译less,又能够压缩、重命名css和js,同时能够压缩html并自动添加版本号的gulp.js配置文件,固然也是参考了原做者的方法: //引入gulp和gulp插件 var gulp = require('gulp'), less = require('gulp-less'), assetRev = require('gulp-asset-rev'), minifyCss = require('gulp-minify-css'), uglify = require('gulp-uglify'), htmlmin = require('gulp-htmlmin'), rename = require('gulp-rename'), imagemin = require('gulp-imagemin'), runSequence = require('run-sequence'), rev = require('gulp-rev'), revCollector = require('gulp-rev-collector'); //定义css、js源文件路径 var cssSrc = 'css/*.css', cssMinSrc = 'dist/css/*.css', jsSrc = 'js/*.js', jsMinSrc = 'dist/js/*.js', lessSrc = 'less/*.less', imgMinSrc = 'dist/images/*.{png,jpg,gif,ico}', htmlSrc = '*.html'; //编译less 定义一个less任务(自定义任务名称) gulp.task('less', function(){ return gulp.src(lessSrc) //该任务针对的文件 .pipe(less()) //该任务调用的模块 .pipe(gulp.dest('css'));//编译后的路径 }); //为css中引入的图片/字体等添加hash编码 gulp.task('assetRev', function(){ return gulp.src(cssSrc) //该任务针对的文件 .pipe(assetRev()) //该任务调用的模块 .pipe(gulp.dest('src')); //编译后的路径 }); //压缩css gulp.task('cssMin', function() { return gulp.src(cssSrc) //压缩的文件 .pipe(rename({suffix: '.min'})) .pipe(minifyCss()) //执行压缩 .pipe(gulp.dest('dist/css')); //输出文件夹 }); //CSS生成文件hash编码并生成 rev-manifest.json文件名对照映射 gulp.task('revCss', function(){ return gulp.src(cssMinSrc) .pipe(rev()) //文件名加MD5后缀 .pipe(rev.manifest()) //必须有这个方法 生成一个rev-manifest.json .pipe(gulp.dest('dist/css')); //将rev-manifest.json 保存到 dist/css 目录内 }); //压缩js gulp.task('uglify',function(){ return gulp.src(jsSrc) .pipe(rename({suffix: '.min'})) .pipe(uglify()) .pipe(gulp.dest('dist/js')); }); //js生成文件hash编码并生成 rev-manifest.json文件名对照映射 gulp.task('revJs', function(){ return gulp.src(jsMinSrc) .pipe(rev()) .pipe(rev.manifest()) .pipe(gulp.dest('dist/js')); }); //压缩html gulp.task('htmlMin',function(){ var options = { collapseWhitespace:true, //从字面意思应该能够看出来,清除空格,压缩html,这一条比较重要,做用比较大,引发的改变压缩量也特别大。 collapseBooleanAttributes:true, //省略布尔属性的值,好比:<input checked="checked"/>,那么设置这个属性后,就会变成 <input checked/>。 removeComments:true, //清除html中注释的部分,咱们应该减小html页面中的注释。 removeEmptyAttributes:true, //清除全部的空属性。 removeScriptTypeAttributes:true, //清除全部script标签中的type="text/javascript"属性。 removeStyleLinkTypeAttributes:true, //清楚全部Link标签上的type属性。 minifyJS:true, //压缩html中的javascript代码。 minifyCSS:true //压缩html中的css代码。 }; return gulp.src(htmlSrc) .pipe(htmlmin(options)) .pipe(gulp.dest('dist/html')); }); //Html替换css、js文件版本 gulp.task('revHtml', function () { return gulp.src(['dist/**/*.json', 'dist/html/*.html']) .pipe(revCollector()) .pipe(gulp.dest('dist/html')); }); //压缩image gulp.task('imageMin', function () { gulp.src('images/*.{png,jpg,gif,ico}') .pipe(imagemin()) .pipe(gulp.dest('dist/images')); }); gulp.task('revImage', function(){ return gulp.src(imgMinSrc) .pipe(rev()) .pipe(rev.manifest()) //必须有这个方法 .pipe(gulp.dest('dist/images')); }); gulp.task('default', function (done) { //condition = false; runSequence( //此处不能用gulp.run这个最大限度并行(异步)执行的方法,要用到runSequence这个串行方法(顺序执行)才能够在运行gulp后顺序执行这些任务并在html中加入版本号 'less', 'assetRev', 'cssMin', 'revCss', 'uglify', 'revJs', 'imageMin', 'revImage', 'htmlMin', 'revHtml', done); });
|
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' ] ))