详解前端自动化工具gulp自动添加版本号


Posted in Javascript onDecember 20, 2016

之前,我介绍了学习安装并配置前端自动化工具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文件代码

<link rel="stylesheet" href="../css/default.css">
<script src="../js/app.js"></script>

预期效果:在原目录结构下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>

有没有发现,会在版本号后面再添加一个版本号,因为gulp只替换了原来文件名,这样又不符合预期效果了,所以我们想到,还需要修改插件的替换正则表达式。

4、继续更改gulp-rev-collector

打开node_modules\gulp-rev-collector\index.js

第107行 regexp: new RegExp( '([\/\\\\\'"])' + pattern, 'g' ),

更新为: regexp: new RegExp( '([\/\\\\\'"])' + pattern+'(\\?v=\\w{10})?', 'g' ),

现在你不管执行多少遍gulp命令,得到的html效果都是

<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才可以给html中引入的图片添加版本号,所以还在摸索中,也请大神给指点指点,谢谢!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
关于javascript 回调函数中变量作用域的讨论
Sep 11 Javascript
JQuery 操作select标签实现代码
May 14 Javascript
Javascript模块化编程(三)require.js的用法及功能介绍
Jan 17 Javascript
自动最大化窗口的Javascript代码
May 22 Javascript
JavaScript程序开发之JS代码放置的位置
Jan 15 Javascript
jQuery遍历DOM节点操作之filter()方法详解
Apr 14 Javascript
Avalon中文长字符截取、关键字符隐藏、自定义过滤器
May 18 Javascript
bootstrap 点击空白处popover弹出框隐藏实例
Jan 24 Javascript
vue-video-player 通过自定义按钮组件实现全屏切换效果【推荐】
Aug 29 Javascript
Bootbox将后台JSON数据填充Form表单的实例代码
Sep 10 Javascript
使用js获取身份证年龄的示例代码
Dec 11 Javascript
Node实现搜索框进行模糊查询
Jun 28 Javascript
详解Vue.js动态绑定class
Dec 20 #Javascript
浅谈Angular的$q, defer, promise
Dec 20 #Javascript
BootStrapTable服务器分页实例解析
Dec 20 #Javascript
bootstrap实现每隔5秒自动轮播效果
Dec 20 #Javascript
bootstrap多种样式进度条展示
Dec 20 #Javascript
JS封装通过className获取元素的函数示例
Dec 20 #Javascript
常用JS图片滚动(无缝、平滑、上下左右滚动)代码大全(推荐)
Dec 20 #Javascript
You might like
一个连接两个不同MYSQL数据库的PHP程序
2006/10/09 PHP
php生成局部唯一识别码LUID的代码
2012/10/06 PHP
php用户注册页面利用js进行表单验证具体实例
2013/10/17 PHP
在JavaScript中遭遇级联表达式陷阱
2007/03/08 Javascript
JS 日期验证正则附asp日期格式化函数
2009/09/11 Javascript
DIV外区域Click后关闭DIV的实现代码
2011/12/21 Javascript
js检验密码强度(低中高)附图
2014/06/05 Javascript
在JavaScript里防止事件函数高频触发和高频调用的方法
2014/09/06 Javascript
jquery和js实现对div的隐藏和显示方法
2014/09/26 Javascript
分享两个手机访问pc网站自动跳转手机端网站代码
2020/12/24 Javascript
jQuery使用slideUp方法实现控制元素缓慢收起
2015/03/27 Javascript
批量下载对路网图片并生成html的实现方法
2016/06/07 Javascript
Bootstrap Modal对话框如何在关闭时触发事件
2016/12/02 Javascript
JavaScript正则获取地址栏中参数的方法
2017/03/02 Javascript
react native与webview通信的示例代码
2017/09/25 Javascript
AngularJS实现注册表单验证功能
2017/10/16 Javascript
vue 运用mock数据的示例代码
2017/11/07 Javascript
JavaScript实现修改伪类样式
2017/11/27 Javascript
MUI 实现侧滑菜单及其主体部分上下滑动的方法
2018/01/25 Javascript
vue.js实现只弹一次弹框
2018/01/29 Javascript
Angular6 Filter实现页面搜索的示例代码
2018/12/02 Javascript
vue实现记事本功能
2019/06/26 Javascript
Vue动态加载图片在跨域时无法显示的问题及解决方法
2020/03/10 Javascript
JS如何定义用字符串拼接的变量
2020/07/11 Javascript
[01:03:38]2014 DOTA2国际邀请赛中国区预选赛5.21 CNB VS CIS
2014/05/22 DOTA
[01:15:12]DOTA2上海特级锦标赛主赛事日 - 1 败者组第一轮#4Newbee VS CDEC
2016/03/03 DOTA
初探利用Python进行图文识别(OCR)
2019/02/26 Python
python Yaml、Json、Dict之间的转化
2020/10/19 Python
如何利用python 读取配置文件
2021/01/06 Python
生物专业个人自荐信范文
2013/11/29 职场文书
培训主管的职业生涯规划
2014/03/06 职场文书
六一儿童节标语
2014/10/08 职场文书
离开雷锋的日子观后感
2015/06/09 职场文书
2016党校学习心得体会
2016/01/07 职场文书
python实现图片批量压缩
2021/04/24 Python
mysql数据插入覆盖和时间戳的问题及解决
2022/03/25 MySQL