《gulp-rev-collector替换不生效解决方案》
之前在使用gulp做构建工具时,针对的项目结构,路径格式比较规范,所以一切顺利。
只遇到一个问题,是在生成rev-manifest.json文件之后发现路径位置不正确。
解决办法是对全局html文件中的样式引用路径格式做了规范化。
在最近对公司一个老的项目做基于gulp的优化加速时,遇到了一些新的问题:

一.html内文件引用与manifest不匹配,导致替换失败:

和开始讲的差不多,因为gulp-rev是根据目录结构生成对应json,
导致gulp-rev生成的rev-manifest.json文件内容与html内的引用方式不一致,举个栗子:

//file rev-manifest.json
{
	"css/style.css" : "css/style-4s77d9as.min.css"
}
//file index.html
<link rel="stylesheet" type="text/css" href="/css/style.css" />

发现了吗?上下的对比中少了一层根目录(’/’)
gulp-rev-collector这个组件只会做强匹配,只有完全一致才会去替换,怎么办?
我自己也上网找了一下解决方案,发现有很多人会出现这样的问题,
有的人在index.html中使用相对路径引用,差别更大。
vpn到期了还没续费,只在百度搜了一圈,解决方案更多偏向的是改源码,
修改gulp-rev-collector源码的匹配替换方式,但我觉得这样会对以后的维护升级留下坑。
最后我的解决方案比较粗暴,直接修改json内容,在进行替换之前使用自己的规则对json进行格式化
直接看代码:

const fs   = require('fs');
const Util = {
	// 对manifest文件进行格式化
	formatJSON : (path,dir) => {
		let mainfestJSON = JSON.parse( fs.readFileSync(path, 'utf8') );
		let newJson = {};
		for(let mainKey in mainfestJSON){
			newJson[dir + mainKey] = dir + mainfestJSON[mainKey];
		}
		fs.writeFileSync(path, JSON.stringify(newJson, null, 4));
	}
}

gulp.task('cssReplace', () => {
	//使用加了MD5之后的css替换页面中引用的标签
	Util.formatJSON('build/rev/css/rev-manifest.json','/');
	return gulp.src([build/rev/css/rev-manifest.json, 'build/**/*.html'])
			.pipe(revCollector())
		    .pipe(gulp.dest('build'));
});

采取同步文件读取的方式,在替换任务执行之前对json直接进行格式化
当然,我的场景比较简单,复杂的场景可以对formatJSON进行优化补充。
不使用fs也可以使用gulp-tap进行过滤,简单写一下,仅实现思路,代码还未测试:

const tap = require('gulp-tap'),//src 文件遍历

gulp.task('formatJSON', () => {
	return gulp.src('build/rev/**/*.json')
		.pipe(tap((file) => {
			let mainfestJSON = JSON.parse(file.contents.toString());
			let newJson = {};
			for(let mainKey in mainfestJSON){
				newJson['/' + mainKey] = '/' + mainfestJSON[mainKey];
			}
			file.contents = new Buffer(JSON.stringify(newJson, null, 4));
		}))
		.pipe(gulp.dest('build'));
});

利用gulp-tap可以对gulp.src()读取的文件进行过滤的特性,
对每一个json进行格式化,大致思路是这样,选哪一套来实现都可以。
做到这一步,rev-manifest.json与html内的标签内容一致便可以替换成功。
但问题又来了,老的项目不只有html,还有一些jsp,shtml,vm这类页面模板
在页面引用与manifest.json强匹配的情况下,html替换成功,jsp,shtml,vm则没有替换成功。

二.不是标准html的情况下,gulp-rev-collector替换失败

上面的情况出现后,我一直认为是我使用错误,或许有什么配置项是需要针对不同场景单独配置的。
毕竟这应该是一个很常见的场景,不应该不支持。
但是尝试了一轮下来之后,发现确实是不行。
去npm搜索了一下gulp replace,gulp template,gulp jsp这些关键字
结果还是没有找到满足我需求的组件,也许有,但是英文水平不好可能错过了。
也不想在这个事情上花费太多时间。干脆还是自己写一个手动替换方法吧。
大致思路是使用gulp-tap对每一个文件进行手动替换。上代码:

const Util = {
    // 使用tap对模板文件进行替换
	replaceTemplate : (gulpFile,replaceJsonObj) => {
		let contentStr = gulpFile.contents.toString();
		for(let replaceKey in replaceJsonObj){
			let reg = new RegExp(replaceKey, 'g');
			contentStr = contentStr.replace(reg, replaceJsonObj[replaceKey]);
		}
		return new Buffer(contentStr);
	}
}
gulp.task('cssReplace', () => {
	//使用加了MD5之后的css替换页面中引用的标签
	let manifest = require('./build/rev/css/rev-manifest.json');
	return gulp.src('build/**/*.{html,shtml,jsp,vm}')
			.pipe(tap((file) => {
				file.contents = Util.replaceTemplate(file, manifest);
			}))
		    .pipe(gulp.dest('build'));
});

比较简单,对原流程的改动不大,并且对于js的替换也可以支持。
对于这种手动替换的方式,总结起来就是简单粗暴,我也曾有一点担心
是否有考虑不到的场景,是否会对文件原本编码有影响,是否对替换效率有影响等等
但是经过测试,发现它的性能还比较不错,基本与gulp-rev-collector替换时间一致
并且一一检查了替换之后的文件,替换结果100%正确,并且可以省去问题一的解决步骤。
后续可以考虑对功能以及拓展性做一些优化,把它提交给npm试试效果。大家等我消息。。

做到这一步,文件压缩替换压缩这个流程基本没什么问题了。但是还有一个场景,
是测试环境与生产环境需要不同的执行流程,测试需要加入sourceMap文件,生产不需要。
生产环境需要压缩代码,而测试环境不需要等等问题

三.通过Gulp执行命令携带参数:

var options = minimist(process.argv.slice(2), {
	  string: 'env',
	  default: { env: process.env.NODE_ENV || 'production' }
	});
const CONFIG = {
		isPro:(options.env === "production")                 //是否生产环境
	}

这样就可以通过 gulp taskName –env production 来改变内部的变量
从而将测试环境与生产环境作区分。

四.总结

文章的内容比较浅,没有很深需要理解半天的内容,代码也比较简单,
我也想炫一下技,写一些高深莫测的代码,但是本领还没学到家。
我的目的是分享解决问题的思路以及实现,达到目的就可以。
Gulp在使用上比较灵活,需要我们在使用现成的还是自己实现这两种选择中更好的作出判断,做出选择
这个过程比较花时间,但是也比较好玩。每次都会有不同的场景,不同的问题等着你去解决。
也欢迎大家将遇到的问题抛出来,我们一起研究,一起解决。
最近一段时间就会将评论放开,方便大家做一些讨论,
之前的评论需要留下邮箱,这可能会因为我的安全问题导致大家的邮箱泄露。
所以在这之前,有问题还需要大家发送到我的邮箱。