時下grunt非常的火啊,用著雖然很爽,但是它的配置確實很煩。如果之前沒有用過,想要一下子熟練駕馭它,有一定的學習成本,而且還要裝node這個大家伙,項目之初我們選擇了compiler.jar這個輕量的工具進行打包。我一直在尋思著,如何編寫一鍵打包工具。之前呢是手工的拼接好有的js文件,做成符合compiler.jar打包文件所要求的批處理文件,然後運行這個批處理,生成我們需要的js和css文件。隨著js文件數量的增長,純手工拼接這些文件的地址就變得非常考驗人的耐心了,而且還容易漏掉或重復某些文件,於是“一鍵打包工具”的編寫就變得刻不容緩了。下面是一個真實的index.html文件的一部分:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="target-densitydpi=device-dpi, initial-scale=1, user-scalable=0, maximum-scale=1"> <!--隱藏浏覽器的工具欄和菜單欄,對iso系統起用--> <!--用於PC上調式,不參與合並壓縮--> <script src="lib/data/database.js" name="noBuild"></script> <!--Iframe加載處理--> <script src="lib/LoadMode.js"></script> <script src="lib/core/jQuery.js"></script> <script src="lib/core/underscore.js"></script> <script src="lib/animate/SVGIcons/snap.min.js"></script> <script src="lib/animate/pixi.js"></script> <script src="lib/core/Xut.js"></script> <script src="lib/core/isMobile.js"></script> <script src="lib/core/aaronRequire.js"></script> <script src="lib/core/nextTick.js"></script> <script src="lib/Config.js"></script> <script src="lib/core/lang/Object.js"></script> <script src="lib/core/lang/Function.js"></script> <script src="lib/core/lang/Array.js"></script> <script src="lib/core/video.js"></script> <!-- 自定義事件,合集處理,iframe通訊 --> <script src="lib/core/event/asEvented.js"></script> <script src="lib/core/message/pms.js"></script> <!--插件--> <script src="lib/plugin/cordova.js"></script> <script src="lib/plugin/readAssetsFilePlugin.js"></script> <script src="lib/plugin/initDatabase.js"></script> <script src="lib/plugin/web.js"></script> <script src="lib/plugin/video.js"></script> <script src="lib/plugin/openAppPlugin.js"></script> <script src="lib/plugin/tabletPlugin.js"></script> <script src="lib/plugin/statusbar.js"></script> <script src="lib/plugin/iap.js"></script> <script src="lib/plugin/AppStoreLink.js"></script> <script src="lib/plugin/downloadPlugin.js"></script> <script src="lib/plugin/xxteManager.js"></script> <script src="lib/plugin/unzipPlugin.js"></script> <script src="lib/plugin/readPlugin.js"></script> <script src="lib/plugin/deletePlugin.js"></script> <!-- 動畫庫 --> <script src="lib/animate/TweenMax.min.js"></script> <script src="lib/animate/plugins/ThrowPropsPlugin.min.js"></script> <script src="lib/animate/PptAnimation.js"></script> <script src="lib/animate/CanvasAnimation.js"></script> <script src="lib/animate/dragdrop/Draggable.min.js"></script> <script src="lib/animate/dragdrop/dragdrop.js"></script> <script src="lib/animate/iscroll.js"></script> <script src="lib/animate/hammer.js"></script> <script src="lib/animate/SVGIcons/svgicons-config.js"></script> <script src="lib/animate/SVGIcons/svgicons.js"></script> <script src="lib/animate/SpriteA.js"></script> <script src="lib/util/Utils.js"></script> <script src="lib/util/LocalStorage.js"></script> <script src="lib/util/ScriptLoad.js"></script> <script src="lib/util/ExecuteSql.js"></script> <script src="lib/util/PromptNotice.js"></script> <script src="lib/util/edge.js"></script> <!-- 配置文件,數據文件,結構文件 --> <script src="lib/data/Store.js"></script> <script src="lib/data/StoreManager.js"></script> <!-- 數據初始化 --> <script src="lib/Main.js"></script> <script src="lib/Initialize.js"></script> <script src="lib/scenario/SceneLayout.js"></script> <script src="lib/scenario/SceneFactory.js"></script> <script src="lib/scenario/SceneController.js"></script> <script src="lib/LoadScene.js"></script> <script src="lib/Dispatcher.js"></script> <!-- 工具欄 --> <script src="lib/toolbar/Navbar.js"></script> <script src="lib/toolbar/sToolbar.js"></script> <script src="lib/toolbar/fToolbar.js"></script> <script src="lib/toolbar/searchBar.js"></script> <script src="lib/toolbar/bookMark.js"></script> <!-- 多線程任務片 --> <script src="lib/threadTask/Buffer.js"></script> <script src="lib/threadTask/TaskContents.js"></script> <script src="lib/threadTask/TaskComponents.js"></script> <script src="lib/threadTask/TaskBackground.js"></script> <script src="lib/threadTask/TaskContainer.js"></script> <script src="lib/pageBase/Parser.js"></script> <script src="lib/pageBase/Collection.js"></script> <script src="lib/pageBase/MultiEvent.js"></script> <script src="lib/pageBase/PageBase.js"></script> <script src="lib/pageBase/Page.js"></script> <script src="lib/pageBase/Master.js"></script> <!-- 頁面管理模塊 --> <script src="lib/controller/transform/Translation.js"></script> <script src="lib/controller/OverrideApi.js"></script> <script src="lib/controller/Abstract.js"></script> <script src="lib/controller/Emitter.js"></script> <script src="lib/controller/PageMgr.js"></script> <script src="lib/controller/MasterMgr.js"></script> <script src="lib/controller/Compiler.js"></script> <script src="lib/controller/ViewModel.js"></script> <script src="lib/controller/SwitchPage.js"></script> <script src="lib/controller/EventDrive.js"></script> <!--熱點管理--> <script src="lib/scheduler/AssignAutoRun.js"></script> <script src="lib/scheduler/AssignTrigger.js"></script> <script src="lib/scheduler/AssignSuspend.js"></script> <script src="lib/scheduler/AssignOriginal.js"></script> <script src="lib/scheduler/AssignRecovery.js"></script> <script src="lib/scheduler/ProcessControl.js"></script> <script src="lib/scheduler/Binding.js"></script> <!-- 適配器,用於處理熱點 --> <script src="lib/directives/dir-Content.js"></script> <script src="lib/directives/dir-Widget.js"></script> <script src="lib/directives/dir-Media.js"></script> <script src="lib/directives/dir-Action.js"></script> <script src="lib/directives/dir-ShowNote.js"></script> <!--多媒體對象 --> <script src="lib/component/media/Audio.js"></script> <script src="lib/component/media/Video.js"></script> <script src="lib/component/media/AudioManager.js"></script> <script src="lib/component/media/VideoManager.js"></script> <!--文本熱點--> <script src="lib/component/content/conFilter.js"></script> <script src="lib/component/content/conAlgorithm.js"></script>
後面還有很長,bug,活人不能被尿憋死,辦法總比問題多。我是會一點php的,php在處理文件方面是很拿手的,是時候讓它發揮點作用了。於是我想到了用php去自動提出index裡邊的js和css,然後按指定的格式生成批處理文件,在後台用靜默方式運行這個批處理,最後把結果返回給顯示器。這樣我就可以坐享其成了。想想都有點小激動喲,於是簡單的寫了一個界面.
接下來就是實現功能了,先不著急編寫代碼,分析下需求:
1. 遍歷index.html文件,提取js文件或css文件.
2. 對這文件進行過慮,因為有些是注釋掉的,有些是調式用的。
3. 生成對應的批處理文件
4. 執行批處理文件
5. 顯示處理結果
即然有兩種情況,我就用一個工廠模式來適配,方便以後擴展其它類型,目光要放長遠一點。即然工廠都有了,那索性再來一個接口,約定都必需要實現“接收請求”和“輸出結果”這兩個接口。再想想處理js和css都可能會有相同的功能,讓它們繼承一個父類可以使代碼復用,於是繼承也先用上。目前的設計應當可以滿足我的要求了,於是開始編寫php代碼.
<?php header("Content-type: text/html; charset=utf-8"); /** * 根據index.html文件中引用的js,生成compressJs.bat * @author frog <[email protected]> * @date 2014-11-17 */ interface ICompress { /** * 處理用戶請求 * @return [type] [description] */ public function request(); /** * 處理輸出結果 * @return [type] [description] */ public function render(); } class BaseCompress { public $content; public $outPath; public $isAuto; public function __construct($isAuto=false){ $outPath = '_file'; if(!is_dir($outPath)){ mkdir($outPath); } $this->outPath = $outPath; $this->isAuto = $isAuto; } /** * 運行批處理 * @return [type] [description] */ public function runBat($name){ } } /** * 壓縮javascript文件 * 合並javascript文件 */ class CompressJS extends BaseCompress implements ICompress { /** * 處理用戶請求 * @return [type] [description] */ public function request(){ } /** * 處理輸出結果 * @return [type] [description] */ public function render(){} } /** * 壓縮樣式文件 */ class CompressCSS extends BaseCompress implements ICompress { public function request(){ } /** * 處理輸出結果 * @return [type] [description] */ public function render(){} } /** * 工廠類 */ class Factory { public static function create($type,$isAuto){ $ob = null; switch ($type) { case 'js': $ob = new CompressJS($isAuto); break; case 'css': $ob = new CompressCSS($isAuto); break; default: # code... break; } return $ob; } }
然後是調用處理:
//處理ajax請求 if(isset($_POST['submit'])){ //是否自動執行批處理 $isAuto = $_POST['zip'] === 'true' ? true : false; //處理類型 $type = $_POST['type']; $c = Factory::create($type,$isAuto);
//接口方法 $c->request(); $c->render(); }else{ echo '請使用靜態頁訪問本程序:<a href="index.html">點此進入</a>'; }
大致的骨架就出來了。具體的填碼過程就比較簡單了。稍微有點難度的就是執行批處理這個一方法.
是網上提供的方法,以後可能用的上,這裡特意貼一下:
public function runBat($name){ if(!file_exists($name) || !$this->isAuto){ return; } //轉入後台處理 @exec(pclose(popen("start /B ". escapeshellcmd($name), "r"))); }
在前面的index.html中,有一個name="noBuild"這個是我人為添加的,這是因為我要過濾這種標識的js文件,這樣以後要過濾別的文件,也只要添加這個標識就可以了,不用改php代碼。由於是內部使用,沒有做表單項來指定工程的路徑,默認就是index.html所處的上級目錄即為工程目錄。這樣設計簡化了操作,提高了效率。
關於遍歷特定的文件,我推薦glob函數,很簡潔的方案:
$files = glob($path.'/*.css',GLOB_NOSORT);
下面是工程目錄結構圖:
再來一張運行效果圖:
最後是處理結果圖:
終於不用寫grunt的配置和安裝node這個家伙了,再也不擔心更新的時候,從svn上拉下來一堆node的東西,是時候和它們說再見了:
好像忘了展示前端代碼了:
/** * 選項卡類 * @param {string} id 選項卡的ID */ function Tabs(id){ var node = document.querySelector('#'+id); var selected = node.querySelector('.selected'); this.selected = selected; this.node = node; this.bindEvent(); } /** * 切換選項卡 * @param {object} event 事件 * @return {[type]} [description] */ Tabs.prototype.change = function(event){ var element = event.target; if(element.tagName.toLowerCase()=='li'){ if(element.className=='selected'){ return; } }else{ while(element != this.node){ element = element.parentNode; if(element.tagName.toLowerCase()=='li'){ break; } } if(element == this.node) return; } this.selected.removeAttribute('class'); element.className = 'selected'; this.selected = element; this.content(); } Tabs.prototype.content = function(){ var form = document.form1; switch(this.getTabType()){ case 'js': form.style.display = 'block'; form.children[0].innerHTML = '自動壓縮JS'; break; case 'css': form.style.display = 'block'; form.children[0].innerHTML = '自動壓縮CSS'; break; default: } }
會前端和後端,就是這麼任性。輕輕一點,告別煩惱!造自己的工具,讓別人去苦逼吧,So easy!