以前,我介紹了學習安裝並配置前端自動化工具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' ] ))