利用gulp+requirejs解决了目前项目的两个主要问题。
静态资源缓存问题
requirejs对于js文件的版本号是通过config.js配置文件中的urlArgs参数统一管理的。
requirejs.config({
...
urlArgs:"v=1.0.0"
path:{
"module1":"./module1",
"module2":"./module2"
}
...
})
requirejs会在装载module1和module2时,在请求后面拼接上"?v=1.0.0"。
问题在于,每次发布,不管哪个环境,都需要手动去修改urlArgs这个参数,否则就会出现缓存问题。
而希望实现的目标是,所有静态资源根据MD5码生成版本号。
解决思路:
一种是利用gulp将配置文件调整成以下样子:
requirejs.config({
...
urlArgs:""
path:{
"module1":"./module1.js?v=dFe82Pzk",
"module2":"./module2.js?v=1a0Ak9pY"
}
...
})
大致实现流程:
1.读取配置文件,将文件中path解析成JSON对象,前提条件是配置文件中path的键值都是string且都用引号引起来,否则无法转换成json对象;
//获取requireCOnfig中的paths JSON对象
const __getRequireConfigPaths = function (file) {
let configContents = file.contents.toString();
let matches = /"paths": (\{[^}]*}),/.exec(configContents);
return JSON.parse(matches[1]);
};
2.遍历所有require管理的js文件进行MD5编码,建立moduleName:md5键值对构成的对象verMap;
//根据文件内容计算md5串
const __getDataMd5 = function (data) {
return crypto.createHash('md5').update(data).digest('base64');
};
//根据paths JSON对象生成[{moduleName:moduleNamev?=MD5}]对照关系verMap
gulp.task("createMD5VerMap", function (cb) {
gulp.src("./config/common-config.js")
.pipe(through2.obj(function (file, encoding, done) {
let paths = __getRequireConfigPaths(file);
//将through2异步操作封装成promise对象,利用Promise.all等待所有through2异步任务执行完毕后调用gulp.task的callback
let promises = [];
for (let moduleName in paths) {
let filePath = './' + paths[moduleName];
let suffix = '';
if(/([Cc]ss$)/.test(moduleName)){
//需约定moduleName以css或者Css结尾对应的都是css文件
filePath = './' + paths[moduleName]+'.css';
suffix = '.css'
}else if(!/(\.html$)/.test(filePath)){
//需约定文件名不以.html结尾的都是js文件
filePath = './' + paths[moduleName]+'.js';
suffix = '.js'
}
promises.push(
new Promise(function (resolve) {
gulp.src(filePath)
.pipe(through2.obj(function (file) {
verMap[moduleName] = paths[moduleName]+suffix+"?v="+__getDataMd5(file.contents).slice(0, 6);
resolve();
}, function () {
//不处理失败任务
console.log("未获取到文件:"+paths[moduleName])
verMap[moduleName] = paths[moduleName];
resolve();
})
)
})
);
}
Promise.all(promises).then(cb());
})
);
});
这里利用Promise.all来保证全部文件完成MD5编码后再进行后续处理,防止异步问题导致verMap未完整生成就被拿去做其他处理。
3.读取配置文件,根据verMap改写path中的值("module1":"./module1" => "module1":"./module1?v=dFe82Pzk")
//将[{moduleName:MD5}]对照关系verMap写入requireConfig.js配置文件中
gulp.task("modifyRequireConfig", function () {
return gulp.src("js/requirejs-config.js")
.pipe(through2.obj(function (file, encoding, done) {
let contents = file.contents.toString();
contents = contents.replace(/"paths": (\{[^}]*}),/, '"paths": '+JSON.stringify(verMap));
file.contents = new Buffer(contents);
this.push(file);
done();
}))
.pipe(rename("requirejs-config.js"))
.pipe(gulp.dest("./js"));
});
这种方法的弊端在于静态资源的覆盖率,对于未在path中配置的静态文件,是没办法打上md5版本号的,所以需要对所有用require或者define引入的js文件,全部写到path里去,导致配置文件臃肿。
网上还有种思路是修改require.js源码,将urlArgs改为允许传入Function,在根据所有静态文件生成moduleName:MD5键值对以后,通过动态获取MD5码来拼接url。(参考:http://www.tuicool.com/articl...)
如果对修改源码没有限制,可以采用这种方式。
代码合并、压缩
利用requirejs的optimize方法,可以将存在相互依赖关系的几个js合并成单个js文件,并提供uglify压缩。
module1:
define("module1", function(){
return {
key: "value"
}
});
module2:
require(["module1"], function(module1){
console.log(module1.key);
})
gulpfile.js
gulp.task("concat", function(){
rjs.optimize({
baseUrl: "./",
name: "./test/module2.js",
out: "./test/app.js",
optimize: "uglify"//压缩
}, function () {
console.log(name + ":" + out + " is OK!");
});
});
执行gulp任务后会将module1和module2合并生成app.js。
app.js:
define("module1",[],function(){return{key:"value"}}),define("test/module1.js",function(){}),require(["./module1.js"],function(e){console.log(e.key)}),define("test/module2.js",function(){});
而对于没有相互依赖关系的js,可以用gulp-concat进行合并,再用gulp-uglify压缩
//公共模块合并
gulp.task('buildCommModule', function () {
return gulp.src(["./js/a.js", "./js/b.js"])
.pipe(concat("common.js"))
.pipe(uglify())
.pipe(gulp.dest('./dist/js/'))
})
最后,对于入口html文件,引入js的代码如下
<script src="../js/requirejs/require.min.js"></script>
<script>
//禁用requirejs-config.js缓存
require.s.contexts._.config.urlArgs = new Date().getTime().toString();
require(['../js/requirejs-config'], function () {
require(['task_main']);
});
</script>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。