之前,我介绍了学习安装并配置前端自动化工具Gulp,觉得gulp确实比grunt的配置简单很多,于是我决定再深入学习一下gulp,就去网上查了资料,发现gulp还可以自动添加版本号,这个功能就为我平时在更新css或js时老是在客户端存在缓存导致更新后的效果无法实时展现的苦恼。所以就赶紧去试了一下,果真可以,很高兴啊,真是为项目开发,为效果的快速展现提供了很多的便利。

实现原理:

1、修改js和css文件;

2、通过对js,css文件内容进行hash运算,生成一个文件的唯一hash字符串(如果文件修改则hash号会发生变化);

3、替换html中的js,css文件名,生成一个带版本号的文件名。

原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-asset-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'),
zip = require('gulp-zip'),
revCollector = require('gulp-rev-collector'); //定义css、js源文件路径
var cssOld = 'css/*.css',
cssSrc = 'src/*.css',
cssMinSrc = 'dist/css/*.css',
jsSrc = 'js/*.js',
jsMinSrc = 'dist/js/*.js',
lessSrc = 'less/*.less',
//imgMinSrc = 'dist/images/*.{png,jpg,jpeg,gif,ico}', //这是导致无法给图片添加版本号时所用的路径
imgSrc = 'images/*.{png,jpg,jpeg,gif,ico}', //这是修改后的路径
htmlSrc = '*.html'; //编译less 定义一个less任务(自定义任务名称)
gulp.task('less', function(){
return gulp.src(lessSrc) //该任务针对的文件
.pipe(less()) //该任务调用的模块
.pipe(gulp.dest('src'));//编译后的路径
}); //为css中引入的图片/字体等添加hash编码
gulp.task('assetRev', function(){
return gulp.src(cssOld) //该任务针对的文件
.pipe(assetRev()) //该任务调用的模块
.pipe(gulp.dest('src')); //为css中引入的图片/字体等添加hash编码后的路径
}); //压缩css 这里必须从“编译less”以及“为css中引入的图片/字体等添加hash编码”后输出的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、img文件版本
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,jpeg,gif,ico}')
.pipe(imagemin())
.pipe(gulp.dest('dist/images'));
}); //img生成文件hash编码并生成rev-manifest.json文件名对照映射
gulp.task('revImage', function(){
return gulp.src(imgSrc)
.pipe(rev())
.pipe(rev.manifest()) //必须有这个方法
.pipe(gulp.dest('dist/images'));
}); //打包
gulp.task('zip', function(){
gulp.src('dist/**/*')
.pipe(zip('zipName.zip'))
.pipe(gulp.dest('dist'));
}); gulp.task('default', function (done) {
//condition = false;
runSequence( //此处不能用gulp.run这个最大限度并行(异步)执行的方法,要用到runSequence这个串行方法(顺序执行)才可以在运行gulp后顺序执行这些任务并在html中加入版本号
'less',
'assetRev',
'cssMin',
'revCss',
'uglify',
'revJs',
'imageMin',
'revImage',
'htmlMin',
'revHtml',
'zip',
done);
});

目前,不知为何必需要运行两次gulp才可以给html中引入的图片添加版本号,所以还在摸索中,也请大神给指点指点,谢谢!

关于需要运行两次gulp才可以给html中引入的图片添加版本号的问题,今天我终于知道了原因所在,原因是在给图片添加版本号之前我先做了压缩图片这一步,同时给图片添加版本号时用到的图片路径恰好又是压缩图片后输出的那个路径,而压缩图片花费的时间比较长,导致后续给图片添加版本号的任务revImage在执行时没有找到其路径下的图片,所以第一次执行任务时,给图片添加版本号的效果没有出来,而第二次执行时由于图片已经压缩过,文件夹里也有了压缩后的图片,所以就给引入的图片添加上了版本号,这是我当时考虑问题的思路不对。这里我们可以将给图片添加版本号的任务revImage针对的文件改成图片压缩前所在文件就可以了。其实,我们在用PS切图时已经对图片做了处理,这里再用gulp压缩,虽然经过gulp压缩后的图片的体积相对小了一些,但图片的清晰度应该也是下降了,所以个人觉得是没有必要再压缩了。

最后,关于只压缩修改过的图片的问题:

由于压缩图片比较耗时,在很多情况下我们只修改了某些图片,没有必要压缩所有图片,所以可以使用”gulp-cache”只压缩修改的图片,没有修改的图片直接从缓存文件读取。

具体解决办法可参考(我没有试过,哈哈):gulp教程之gulp-imagemin

这里提醒一下:

有同学在执行了gulp命令后发现html文件中并没有加上版本号,如果你是按照我的配置文件去执行的,那么可能是你html文件中引入的css或js不是打包生成后的css或js。如果你说我就想在原来的html文件中引入的css或js的基础上加版本号,那么你就要修改你的配置了,大概的配置如下:

//引入gulp和gulp插件
var gulp = require('gulp'),
rev = require('gulp-rev'),
revCollector = require('gulp-rev-collector'); //定义css、js源文件路径
var cssSrc = 'css/*.css',
jsSrc = 'js/*.js',
maniFestJsonSrc = 'dist/**/*.json', //所有rev-manifest.json的路径
htmlSrc = './*.html'; //根目录下所有的html //CSS生成文件hash编码并生成rev-manifest.json文件名对照映射
gulp.task('revCss', function(){
return gulp.src(cssSrc)
.pipe(rev()) //文件名加MD5后缀
.pipe(rev.manifest()) //必须有这个方法 生成一个rev-manifest.json
.pipe(gulp.dest('dist/css')); //将rev-manifest.json 保存到 dist/css 目录内
}); //js生成文件hash编码并生成rev-manifest.json文件名对照映射
gulp.task('revJs', function(){
return gulp.src(jsSrc)
.pipe(rev())
.pipe(rev.manifest())
.pipe(gulp.dest('dist/js'));
}); //Html替换css、js、img文件版本
gulp.task('revHtml', function(){
return gulp.src([maniFestJsonSrc, htmlSrc])
.pipe(revCollector())
.pipe(gulp.dest('./')); //输出到根目录
}); //按顺序执行
gulp.task('default',gulp.series(
'revCss',
'revJs',
'revHtml'
));

目录结构如下:

----------------------------------------------------- 这里是分隔符 ---------------------------------------------------------

写在2017年11月28日,加了一个zip打包的任务。

写在2018年7月18日,目前gulp版本已经到4了,而且很多所依赖的插件的版本也都更新了很多,所以再用之前的gulp配置文件中的某些配置来打包压缩文件已经不可能了,除非你还安装之前版本的gulp和其依赖的插件。那么现在就来看看如何更改gulp配置文件来适应gulp4的打包功能。

首先,新版本的gulp所依赖的很多插件都改由es6来实现了,但某些插件的配置文件还是需要我们来手动修改的,以达到我们想要的效果(如果你不想修改也可以),比如这样式的:

但是上图中需要修改的某些地方在其版本更新后,有些我们就找不到了,或者找到了却跟之前的不太一样,不知怎么修改,那么下边就贴出到目前为止最新的这些插件的修改方法。

首先前文中提到的“更改gulp-rev和gulp-rev-collector”的修改方法如下:

第一处、

打开node_modules\gulp-rev\index.js
找到: manifest[originalFile] = revisionedFile;
更新为: manifest[originalFile] = originalFile + '?v=' + file.revHash;

上边这里的修改没有变,还是原来的改法。

第二处、

打开nodemodules\gulp-rev\nodemodules\rev-path\index.js
找到: return filename + '-' + hash + ext;
更新为: return filename + ext;

上边这里的修改就变了,我试了好几次,都没有成功,就索性把之前老版本的代码拷过来了,发现也能用,如下:

打开nodemodules\gulp-rev\nodemodules\rev-path\index.js
找到:return modifyFilename(pth, (filename, ext) => `${filename}-${hash}${ext}`); //这里是es6的写法
更新为:return modifyFilename(pth, (filename, ext) => filename + ext);

第三处、

打开node_modules\gulp-rev-collector\index.js
找到: 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) ) {

上边这里的修改也变了,我也是把之前版本的代码直接拷过来了,如下:

找到:
if (_.isObject(json)) {
var isRev = 1;
Object.keys(json).forEach(function (key) {
if (!_.isString(json[key])) {
isRev = 0;
return;
}
var cleanReplacement = path.basename(json[key]).replace(new RegExp( opts.revSuffix ), '' );
if (!~[
path.basename(key),
_mapExtnames(path.basename(key), opts)
].indexOf(cleanReplacement)
) {
isRev = 0;
}
}); if (isRev) {
data = json;
}
} 更新为:
if (_.isObject(json)) {
var isRev = 1;
Object.keys(json).forEach(function (key) {
if ( !_.isString(json[key]) || path.basename(json[key]).split('?')[0] !== path.basename(key) ) {
isRev = 0;
}
}); if (isRev) {
data = json;
}
}

第四处、

上文中提到的“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' ),

上边这里的修改也变了,具体修改如下:

打开node_modules\gulp-rev-collector\index.js
找到:regexp: new RegExp( prefixDelim + pattern, 'g' ),
更新为: regexp: new RegExp( prefixDelim + pattern +'(\\?v=\\w{10})?', 'g' ),

其次,也是最主要的一点是gulp4不再能够通过数组形式传入任务,你需要使用gulp.series()和gulp.parallel()来执行他们。例如:

gulp.task('default',gulp.parallel('taskA','taskB'));//并行执行
gulp.task('default',gulp.series('taskA','taskB'));//按顺序执行

所以,之前的写法如gulp.task('default', function (done) {runSequence(('taskA','taskB'),done}在gulp4下已经不能使用了,要改成如下的写法:

gulp.task('default',gulp.series(
'less',
'assetRev',
'cssMin',
'revCss',
'uglify',
'revJs',
'imageMin',
'revImage',
'htmlMin',
'revHtml',
'zip'
));

其他的插件需要修改的地方还是上文中提到的改法,这里不做赘述,如下:

打开node_modules\gulp-asset-rev\index.js
找到:var verStr = (options.verConnecter || "-") + md5;
更新为:var verStr = (options.verConnecter || "") + md5;
找到:src = src.replace(verStr, '').replace(/(\.[^\.]+)$/, verStr + "$1");
更新为:src=src+"?v="+verStr;

如此,关于gulp4及其所依赖的插件的修改配置方法已经全部介绍完毕。

本文转自:https://segmentfault.com/a/1190000006204457

DEMO下载:

只在原来的html中引入的css或js的基础上添加版本号

给打包后的html文件中引入的css、js、图片、webFont字体添加版本号

前端自动化工具gulp自动添加版本号的更多相关文章

  1. Gulp自动添加版本号(转载)

    本文转载自: gulp自动添加版本号

  2. 学习安装并配置前端自动化工具Gulp

    Gulp和所有Gulp插件都是基于nodeJs来运行的,因此在你的电脑上需要安装nodeJs,安装过程请移驾安装并配置前端自动化工具--grunt.安装完成后,通过运行cmd进入DOS命令窗口,如图: ...

  3. 前端自动化工具 -- Gulp 使用简介

    gulp是基于流的前端自动化构建工具. 之前也谈到了 grunt的用法,grunt其实就是配置+配置的形式. 而gulp呢,是基于stream流的形式,也就是前一个函数(工厂)制造出结果,提供后者使用 ...

  4. 前端自动化工具 -- gulp https://angularjs.org/

    gulp是基于流的前端自动化构建工具. gulp是基于stream流的形式,也就是前一个函数(工厂)制造出结果,提供后者使用. 同样的,也是包括基本用法和各插件的使用. 二.基本用法--插件使用 gu ...

  5. 安装并配置前端自动化工具-gulp

    由于现在前端自动化已经很有必要了,所以我今天死皮烂脸的找了2位前端大咖帮助我安装和配置gulp,讲真,这一步步弄下来直到安装配置成功,到现在还是迷迷糊糊,不过我还是把这些步骤给记录下来,以防下次不记得 ...

  6. 前端自动化工具 gulp

    最近一个项目才接触这些自动化工具 webpack gulp grunt 等等.. webpack 可以引入模块 和 压缩 gulp 和 grunt 可以压缩 这里只说下gulp  因为项目里只用到gu ...

  7. 【gulp】前端自动化工具---gulp的使用(一)------【巷子】

    什么是gulp?   基于node的自动化构建工具   扩展:开发的时候分为2个节点一个是开发阶段  另一个是部署阶段        开发阶段:源文件不会被压缩            部署阶段:所有文 ...

  8. 前端自动化工具gulp入门基础

    gulp是前端开发过程中经常要用到的工具,非常值得花时间去掌握.利用gulp,我们可以使产品流程脚本化,节约大量的时间,有条不紊地进行业务开发.本文简单讲一下入门gulp需要掌握的东西. 安装gulp ...

  9. Gulp自动添加版本号

    推荐使用gulp-rev + gulp-rev-collector是比较方便的方法,结果如下: "/css/style.css" => "/dist/css/sty ...

随机推荐

  1. windows 下搭建简易nginx+PHP环境

    2016年11月19日 14:40:16 星期六 官网下载 nginx, php windows下的源码包(windows下不用安装, 解压即可) 修改配置文件, (稍后补上) 路径如下: 启动脚本: ...

  2. set和map的简单用法

    .set(集合)map(映射)都属于关联类容器 都支持查询一个元素是否存在并能够有效地获取元素. set集合的元素总是从小到大排列,set集合通过二分查找树实现.它具备以下两个特点: ①:独一无二的元 ...

  3. AOP 面向切面编程, Attribute在项目中的应用

    一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...

  4. AVL Insight 开源情报工具:一站式情报管理服务

    一.概要 AVL Insight 开源情报工具是安天移动安全推出的一款情报收集工具,它是配合AVL Insight移动威胁情报平台的Chrome浏览器扩展程序,用户可以使用该工具,对网站中的公开信息进 ...

  5. ****基于H5的微信支付开发详解[转]

    这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可 ...

  6. php单点登录之模拟淘宝天猫同步登录

    说到单点登录大家都很了解,一个站点登录其他域会自动登录. 单点登录SSO(Single Sign On)的方法有很多,比如:p3p.共享session.共享cookice.第三方OAuth认证. 这里 ...

  7. 【DWR系列04】- DWR配置详解

    table { margin-left: 30px; width: 90%; border: 1px; border-collapse: collapse } img { border: 1px so ...

  8. 【leetcode】Isomorphic Strings

    题目简述: Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the ...

  9. Oracle中生成随机数的函数(转载)

    在Oracle中的DBMS_RANDOM程序包中封装了一些生成随机数和随机字符串的函数,其中常用的有以下两个: DBMS_RANDOM.VALUE函数 该函数用来产生一个随机数,有两种用法: 1. 产 ...

  10. Drools API的使用学习

    Drools API的使用学习在 Drools 当中,规则的编译与运行要通过 Drools 提供的各种 API 来实现,这些 API 总体来讲可以分为三类:规则编译.规则收集和规则的执行.完成这些工作 ...