天天看點

gulp項目建構小結

告别了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..大概就是這樣,暫時先說這麼多,下次待續~