告别了jQuery,现在想分享下我们项目构建所使用的技术。
1、es6
2、browserify
3、babelify
4、gulp
5、concat
6、browser-sync
1、使用es6的模块管理(同时可以使用es6的语法糖了!)
2、使用browserify进行模块打包
3、babelify结合browserify使用,会在browserify打包时,进行语法转换,将ES6的语法转换为ES5。(但不会提供ES6的方法,所以不要用ES6的方法)
4、gulp执行任务。
5、concat进行文件合并。
6、browser-sync监听文件的编号
流程:
1、开发阶段
2、项目部署
在开发阶段,我们配置了一个watch任务,watch任务会将你的代码进行编译、打包,结合browser-sync一起用简直太爽!FileDevManager
/*所有文件的依赖管理器
* 1、在每次更新文件时应该检测依赖对象是否有变化
*/
var FileDevManager = (function(){
var FileDevMaps = [],
updateFileDevMaps = (function(prefix, rimports, rmodule, rfolder){
return function(results){
if(!Array.isArray(results)){
results = [results];
}
results.forEach(function(tmp){
FileDevMaps[tmp] = mapDev(tmp);
});
function mapDev(tmp){
if(!fs.existsSync(prefix+tmp)){
console.log(prefix+tmp, "不存在");
return [];
}
return (fs.readFileSync(prefix+tmp, "utf-8").match(rimports) || [])
.map(function(i){
var ret = i.match(rmodule),
rawModule = ret[0],
module = ret[1] + ".js";
if(0==rawModule.indexOf('./')){
module = tmp.match(rfolder)[0]+module;
}
FileDevMaps[module] = mapDev(module);
return module;
});
}
}
})(rootPath+browserifyPath, /(?:import.*?\.+?\/)(.+?)(?:'|")/g, /(?:\.+?\/)(.+?)(?:'|")/, /.+\/|/),
getFileDevMaps = function(){
return FileDevMaps;
},
build = function(results){
updateFileDevMaps(results);
updateDevsMap();
},
updateDevsMap = function(){
for(var key in FileDevMaps){
FileDevMaps[key] = mapBy(key, FileDevMaps[key]);
}
function mapBy(key, map){
var devBy;
var devs = [];
if('devs' in Object(map)){
devs = map.devs;
devBy = map.devBy;
}else{
devs = map;
devBy = [];
}
for(var k in FileDevMaps){
//console.log(FileDevMaps[k],k)
var current = FileDevMaps[k];
var cdevs = current.devs;
var tmpArr;
if(cdevs){
tmpArr = cdevs;
}else {
tmpArr = current;
}
tmpArr.indexOf(key)!=-1 && devBy.push(k);
}
var map = {
devs: devs,
devBy: devBy
};
return map;
};
}
return {
update: updateFileDevMaps,
updateMap: updateDevsMap,
build: build,
get: getFileDevMaps
};
})();
模块管理的思路很简单:
因为用了ES6,所以所有的模块都是import导入和export导出,所以用了正则去匹配import作为所依赖的模块。
另外提供了更新依赖关系的接口。在每次文件被修改的时候都会重新读取文件内容获取模块之间的依赖关系。
在启动watch任务的时候会构建一个所有文件之间完整的依赖关系,为了降低文件模块对象的关系复杂度,每个模块分别有一个属性devs代表有哪些依赖的模块和devBy代表被哪些模块所依赖。
再看Watch任务:
gulp.watch(rootPath+browserifyPath+'**/*.js', function(modify){
if(!readyToWatch){
return console.log('files are not ready, please wait!');
}
var path = modify.path;
if(!fs.existsSync(path)){
return console.log(path, "不存在!");
}
if(fs.lstatSync(path).isDirectory()){
return console.log(path+" is dir");
}
path = path.replace(/\\/g, "/");
try{
var tmp = path.match(rpath)[1];
}catch(e){
console.log('路径配置有误!');
}
var folder = (tmp.match(/.+\//) || [])[0];
var folderIdx = (onlyCopyPath || []).indexOf( folder );
if(folderIdx != -1){
console.log('only copy ', tmp);
return gulp.src(rootPath+browserifyPath+tmp)
.pipe(gulp.dest(rootPath+copyPath+onlyCopyPath[folderIdx]));
}
if(modify.type === 'added'){
FileDevManager.update(tmp);
FileDevManager.updateMap();
}
var maps = FileDevManager.get();
var devBy = (maps[tmp] || {devBy: []}).devBy || [];
//获取编译队列
var queue = getToES5Queue(devBy.concat(tmp), allToES5Queue);
queue.priority = queue.priority.filter(function(item, idx){
return queue.priority.indexOf(item)===idx;
});
queue.normal = queue.normal.filter(function(item, idx){
return queue.normal.indexOf(item)===idx;
});
function getToES5Queue(devBy, queue){
return devBy.reduce(function(queue, item){
var ret = typeof rmainFile !== 'function' ? rmainFile.test(item) : !!rmainFile(item);
var list = ret ? queue.priority : queue.normal;
list.unshift(item);
getToES5Queue((maps[item] || {}).devBy || [], queue);
return queue;
}, queue);
}
afterToES5(queue.priority, {
resolve: function(file){
reload();
afterToES5(queue.normal);
}
});
function afterToES5(queue, promise){
while(queue.length){
toES5(queue.shift(), promise);
}
}
var updateList = queue.priority.concat(queue.normal).concat(tmp);
updateList.filter(function(a, b){
return updateList.indexOf(a)===b;
}).forEach(function(i){
FileDevManager.update(i);
});
FileDevManager.updateMap();
console.log(tmp, FileDevManager.get()[tmp]);
return false;
});
watch任务会在toES5任务之后,进行update依赖关系。
因为在后期开发的时候发现在当一个文件有太多的依赖(当时用的是React+Redux架构)的时候,文件编译会很慢(2S以上),所以我对编译文件的队列做了处理,将主文件——判断是否是主文件通过rmainFile这个方法/正则来进行判断,主文件会放在priority数组中,会有限进行编译,而且只会有这个一个文件再编译,保证主文件的最快编译速度。
以上基本上是我们项目watch任务所做的所有操作。
2、项目build
项目build相对简单,我们所做的思路很直接,将一个文件夹作为一个任务,build做的事情无法就是文件压缩、加MD5戳。
um..大概就是这样,暂时先说这么多,下次待续~