Grunt擁有數量龐大的插件,這些插件能夠幫助我們處理開發中遇到的絕大多數構建任務,比如代碼的預編譯、壓縮、代碼檢查、單元測試等。但為什么在終端輸入Grunt相關命令,就能夠執行對應的任務,Grunt到底是怎么運轉的?這些知識對于深入研究Grunt非常重要,下面我們從Grunt運轉的組件和運轉機制兩方面來展開討論。
node和npm
Grunt項目基于Node.js,Grunt和相關的插件都通過 npm 安裝并管理。
Grunt-cli
Grunt命令行用于調用與Gruntfile文件在同一目錄中的 Grunt模塊,通過-g參數把Grunt命令行安裝到全局環境中,這樣的話在所有文件目錄中都可以調用grunt相關的命令。
在命令行中運行Grunt 相關命令時(比如 $grunt default),內部會根據node提供的require系統查找來當前目錄中安裝的 Grunt,如果找到那么加載,并把加載的grunt作為參數傳遞到Gruntfile文件中,然后執行指定的任務。
Task
Task就是任務的意思,grunt支持自定義任務,也支持使用現成的插件任務。比如向控制臺輸出一句問候這可以被認為是一個Task,對所有的js文件進行壓縮這也是一個Task,通常任務(Task)都是可配置的。
Grunt本地依賴
安裝了grunt命令行不等于就安裝了grunt,這只是讓我們擁有了在命令行中使用grunt相關命令的能力,對于每個需要使用grunt的工程,仍然需要為其配置grunt本地依賴。
Grunt插件(Plugins)
Grunt插件是一系列能夠用于不同項目的可配置任務的集合。Grunt插件通常以npm包的形式發布。Grunt官網的插件列表列出了所有可用的Grunt插件,截止當前的插件數量為6,393個,其中帶有contrib前綴的插件由Grunt官方開發和維護。
package.json文件
package.json文件用于被npm存儲項目的元數據,以便將此項目發布為npm模塊。我們可以在此文件中列出項目依賴的Grunt和Grunt插件,保存在devDependencies(開發依賴)配置字段內,我們可以通過$ npm install命令來加載該文件中的所有依賴項。
Gruntfile.js文件
Gruntfile文件是Grunt項目中最核心的文件,該文件同package.json文件一起存放在項目的根目錄中,主要用來配置或定義任務(task)并加載Grunt插件。標準的grunt項目中必須擁有package.json和Gruntfile這兩個文件。
node_modules文件夾
node_modules文件目錄存放著從遠程倉庫下載的grunt以及所有相關的grunt插件。
上面給出了Grunt項目中各主要組件的關系圖示,是根據個人的理解繪制的,所以可能并非完全準確,但基本上已經能夠說清楚Grunt的運轉機制了。
我們在使用Grunt作為項目構建工具的時候,所做的事情大概可以分成三塊:準備、配置、執行。
① 準備階段
準備階段主要進行以下操作
? node環境的安裝、npm的安裝(在安裝node的時候默認安裝)
? grunt-cli命令行的安裝(通過$ npm install -g grunt-cli命令)
? 創建package.json文件(手動創建或通過$ npm init命令交互式創建)
? 配置grunt本地依賴(通過$ npm install grunt --save-dev下載grunt到項目)
? 安裝需要的grunt插件(通過$ npm install grunt-contrib-xx --save-dev命令把需要的插件下載到node_modules目錄)
② 配置階段
配置階段主要就是創建和編輯Gruntfile文件,在該文件中接收grunt參數并配置Task,注冊Task。Task簡單說就是任務的意思,我們可以自定義任務,也可以直接使用現成的、一些其他優秀開發者定義好并打包為node模塊發布的任務(其實就是grunt插件)。
一般來說,我們總是通過grunt為我們提供的grunt.initConfig方法來對Task(插件)進行配置,如果是該Task是Grunt插件那么還需要先從node_modules目錄中加載。
如果對多個Task的執行有指定的順序或者依賴關系,那么我們可以通過grunt.registerTask方法來注冊Task。
③ 執行階段
在執行階段,通過在命令行中輸入$ grunt task名稱的方式來執行指定的任務。
執行Task的時候,可以單個執行,例如:
$ grunt taskName1
$ grunt taskName2
也可以用單條命令執行多個Task,每個Task都將按照參數的傳入順序依次執行,例如:
$ grunt taskName1 taskName2
在使用構建工具的時候,這些Task具體怎么執行,執行的順序等并非是固定不變的,需要結合特定的需求來特殊處理。如果總是有一組Task需要按順序執行,一般可以使用grunt.registerTask方法來給這組Task設置個別名,這一組的Task以數組的形式傳遞。
例如:要依次執行js文件的合并、語法檢查、代碼壓縮、css代碼壓縮等任務,則配置好相關Task后可以像下面這樣來設置。
grunt.registerTask("customTask",["concat","jshint","uglify","cssmin"]);
要執行這組任務的時候,直接執行$ grunt customTask命令即可。
在使用Grunt的時候,可以先到Grunt官網的插件列表搜索是否有適合自己項目的Grunt插件,如果有那么建議直接使用,如果沒有那么開發者可以嘗試自定義任務或者是自己創建對應的插件。Grunt的插件其實就是一些封裝好的任務(Task),沒有什么稀奇的,Grunt支持自定義任務,而且方式非常簡單。
如果我們需要定義一個任務,向控制臺里輸出字符串信息,那么在package.json文件、Gruntfile文件已經創建且grunt本地依賴已安裝的前提下,如下編輯Gruntfile文件即可:
//包裝函數 module.exports=function (grunt) { //(1)自定義任務(一) //向控制臺輸出:hello 文頂頂 //第一個參數:任務的名稱(Task) //第二個參數:具體的任務內容 grunt.registerTask("hello",function () { grunt.log.writeln("hello 文頂頂"); }); //(2)自定義任務(二) grunt.registerTask("football",function () { grunt.log.writeln("皇家馬德里: how are you!"); grunt.log.writeln("尤文圖斯: how old are you!"); }); };
終端輸入命令執行任務,可以單個執行,也可以一起執行,下面給出具體執行情況
wendingding:02-Grunt_Test wendingding$ grunt hello Running "hello" task hello 文頂頂 Done. wendingding:02-Grunt_Test wendingding$ grunt football Running "football" task 皇家馬德里: how are you! 尤文圖斯: how old are you! Done. wendingding:02-Grunt_Test wendingding$ grunt hello football Running "hello" task hello 文頂頂 Running "football" task 皇家馬德里: how are you! 尤文圖斯: how old are you! Done.
通過上面的代碼我們可以看到,自定義任務非常簡單,只需要調用grunt對象的registerTask方法即可,其中第一個參數是Task的名稱,第二個參數是回調函數用來存放具體的任務(比如這里是打印輸出)。
在自定義任務中,我們用到了grunt.log.writeln函數,這是Grunt提供的眾多內置方法之一,作用是向控制臺輸出消息并換行。同類型的方法還有grunt.log.error()、grunt.log.subhead()等方法,大家可以到官網API文檔自行查看。
Grunt項目在具體使用的時候,通常是自定義Task + Grunt插件相結合的形式,我們來看下面這段代碼:
//包裝函數 module.exports=function (grunt) { //(1)自定義任務(一) 任務名稱 hello grunt.registerTask("hello",function () { grunt.log.writeln("hello 文頂頂"); }); //(2)自定義任務(二) 任務名稱 football grunt.registerTask("football",function () { grunt.log.writeln("皇家馬德里: how are you!"); grunt.log.writeln("尤文圖斯: how old are you!"); }); //(2) 插件的處理 //使用步驟: //[1] 先把對應的插件下載和安裝到本地的項目中 $ npm install grunt-contrib-concat --save-dev //[2] 對插件(任務)進行配置 grunt.initConfig //[3] 加載對應的插件 loadNpmTasks //[4] 注冊任務 grunt.registerTask //[5] 通過grunt命令執行任務 //配置插件相關信息 grunt.initConfig({ "concat":{ "dist":{ "src":["src/demo_one.js","src/demo_two.js","src/demo_three.js"], "dest":"dist/index.js" } } }); //加載插件 grunt.loadNpmTasks("grunt-contrib-concat"); //注冊任務(一):把hello \ football \ concat 這三個Task注冊為default的Task //當執行$ grunt 或者是$ grunt default的時候,會順序執行者三個任務! grunt.registerTask("default",["hello","football","concat"]); //注冊任務(二) grunt.registerTask("customTask",["hello","football"]); };
對于上面的Gruntfile文件,如果在終端輸入$ grunt或者$ grunt default 命令則依次執行hello football和concat三個任務,輸入$ grunt customTask則一次執行hello football 自定義任務。
設置任務描述
隨著項目復雜性的增加,Grunt任務也會越來越多,而任務(Task)的可用性、用途以及調用方法可能會變得難以追蹤。所幸,我們可以通過給任務設定相應的描述信息來解決這些問題。
要給任務設置描述信息非常簡單,只需要在調用registerTask方法的時候多傳遞一個參數即可(作為第二個參數傳遞),我們可以把一個具體的字符串描述信息作為函數的參數傳遞。
這里,我們修改上面示例代碼中football任務部分的代碼,并任務設置描述信息。
grunt.registerTask("football","17-18賽季 歐冠八分之一決賽抽簽場景",function () { grunt.log.writeln("皇家馬德里: how are you!"); grunt.log.writeln("尤文圖斯: how old are you!"); });
此時,在終端中輸入$ grunt --help命令就能夠看到當前Grunt項目中可用的Task,以及相應的描述信息了,關鍵信息如下。
Available tasks hello Custom task. football 17-18賽季 歐冠八分之一決賽抽簽場景 concat Concatenate files. * default Alias for "hello", "football", "concat" tasks. customTask Alias for "hello", "football" tasks.
任務依賴
在復雜的Grunt工作流程中,很多任務之間往往存在依賴關系,比如js代碼的語法檢查和壓縮這兩個任務,壓縮任務需要依賴于語法檢查任務,它們在執行的時候存在一定的先后關系,這種情況我們稱之為任務依賴。
我們可以在注冊任務的時候,刻意指定這種依賴關系,他們更多的是以一種特定的先后順序執行。如果是自定義任務,也可以通過grunt.task.requires()方法來設定這種任務間的依賴關系。
module.exports=function (grunt) { //注冊兩個自定義任務 /* * 第一個參數:Task的名稱 * 第二個參數:任務的描述信息 * */ grunt.registerTask("hi","描述信息:這是一個打招呼的任務",function () { grunt.log.ok("hi 文頂頂"); }); grunt.registerTask("hello","任務的描述次信息:這是一個簡單問候任務",function () { //設置任務依賴:表明當前的任務在執行的時候需要依賴于另外一個任務 //必須先執行hi這個任務,才能執行hello這個任務 grunt.task.requires("hi"); console.log("Nice to meet you!"); }); };
上面的代碼中定義了hi和hello兩個任務,其中hello這個Task需要依賴于hi的執行,如果直接執行hello,那么會打印任務依賴的提示信息,具體的執行情況如下。
wendingding:05-Grunt項目任務的描述和依賴 wendingding$ grunt hello Running "hello" task Warning: Required task "hi" must be run first. Use --force to continue. Aborted due to warnings. wendingding:05-Grunt項目任務的描述和依賴 wendingding$ grunt hi hello Running "hi" task >> hi 文頂頂 Running "hello" task Nice to meet you! Done.
理解多目標Task
Grunt中的多目標任務(multi-task)是相對于基本任務而言的,多目標任務幾乎是Grunt中最復雜的概念。它的使用方式非常靈活,其設計的目的是可以在當個項目中支持多個Targets目標[可以認為是多種配置]。當任務在執行的時候,可以一次性執行全部的Target也可以指定某一特定的Target執行。
module.exports=function (grunt) { //(1) 配置Task,給Task設置多個Target grunt.config("hello", { "targetA":{ "des":"Nice to meet you!" }, "targetB":{ "des":"how are you?" }, } ); //(2) 自定義任務 任務的名稱為hello //第一個參數:Task名稱 //第二個參數:任務的描述信息 //第三個參數:具體要執行的任務 grunt.registerMultiTask("hello","描述信息:打招呼",function () { grunt.log.ok("hello 文頂頂"); grunt.log.writeln("this.target:",this.target); grunt.log.writeln("this.data:",this.data); }); };
代碼說明
通過觀察可以發現,我們通過grunt.registerMultiTask方法創建了支持多任務(Target)操作的自定義任務hello,主要任務就是輸出“hello 文頂頂”消息以及打印當前的target和data值。然后通過grunt.config方法來給hello這個Task設定了兩個Target,分別是targetA和targetB。
在上面的代碼中,我們引用了this.target和this.data這兩個屬性,回調函數中的this指向的是當前正在運行的目標對象。執行targetA這個選項的時候,打印的this對象如下:
{ nameArgs: 'hello:targetA', name: 'hello', args: [], flags: {}, async: [Function], errorCount: [Getter], requires: [Function: bound ], requiresConfig: [Function], options: [Function], target: 'targetA', data: { des: 'Nice to meet you!' }, files: [], filesSrc: [Getter] }
目前為止,我們一直在談論Task(任務)和Target(目標),大家可能懵逼了,不禁要問它們之間到底是什么關系?
私以為可以簡單的類比一下,假設現在有一個任務就是中午吃大餐,而具體吃什么大餐,可以靈活安排多個方案進行選擇,比如方案A吃西餐,方案B吃中餐,方案C吃日本料理。等我們真正到了餐館要開吃的時候,可以選擇方案A吃西餐或者是方案B吃中餐,甚至中餐、西餐和日本料理全端上桌也未嘗不可。
Task指的是整個任務,在這個例子中就是要吃大餐,Target指的是任務中的某一種可行方案,也就是方案A、方案B和方案C,吃大餐這個Task中我們配置了三個Target。定義任務的目的是為了執行,在執行Task的時候,我們可以選擇執行某個或某幾個指定的Target(目標),這樣的處理方式無疑更強大而且操作起來更加的靈活。
多目標任務的執行
運行多目標Task的時候,有多種方式選擇。
① 讓Task按照指定的target運行。$ grunt TaskName:targetName
② 讓Task把所有的target都運行一次。$ grunt TaskName
下面列出示例代碼的具體執行情況
wendingding:05-Grunt項目任務的描述和依賴 wendingding$ grunt hello Running "hello:targetA" (hello) task >> hello 文頂頂 this.target: targetA this.data: { des: 'Nice to meet you!' } Running "hello:targetB" (hello) task >> hello 文頂頂 this.target: targetB this.data: { des: 'how are you?' } Done. wendingding:05-Grunt項目任務的描述和依賴 wendingding$ grunt hello:targetA Running "hello:targetA" (hello) task >> hello 文頂頂 this.target: targetA this.data: { des: 'Nice to meet you!' } Done. wendingding:05-Grunt項目任務的描述和依賴 wendingding$ grunt hello:targetB Running "hello:targetB" (hello) task >> hello 文頂頂 this.target: targetB this.data: { des: 'how are you?' } Done.
如果在Gruntfile文件中,調用了grunt.registerTask方法來注冊自定義任務,那么可以通過TaskName:targetName的來方式直接指定任務的Target
//注冊任務 [給hello起一個別名] grunt.registerTask("helloTargetA",["hello:targetA"]);
在終端中,輸入$ grunt helloTargetA命令將會執行hello這個Task中的targetA選項。
多目標任務的Options選項
在對多目標的任務進行配置的時候,任何存儲在options選項下面的數據都會被特殊的處理。
下面列出一份Gruntfile文件中的核心代碼,并以多種方式執行,通過這份代碼能夠幫助我們理解多目標任務的Options選項配置。
//包裝函數 module.exports=function (grunt) { //(1) 配置Task相關信息 /* * 第一個參數:Task的名稱 * 第二個參數:任務的描述信息 * */ grunt.initConfig({ "hi": { /*對整個任務中所有target的配置項 全局配置*/ options:{ "outPut":"array" }, targetA:{ arrM:["targetA_1","targetA_2","targetA_3"] }, targetB:{ options:{ "outPut":"json" }, arrM:["targetB_1","targetB_2","targetB_3"] }, targetC:{ arrM:["targetC_1","targetC_2","targetC_3"] } } }); //(2) 自定義任務 Task名稱為hi //第一個參數:Task名稱 //第二個參數:任務的描述信息 //第三個參數:具體要執行的任務 grunt.registerMultiTask("hi","描述次信息:這是一個打招呼的任務",function () { console.log("任務當前執行的target: "+this.target); console.log("任務當前執行的target對應的數據: \n"); var objT=this.options(); if (objT.outPut==="array") { console.log("輸出數組:\n"); console.log(this.data.arrM); }else if (objT.outPut==="json") { console.log("輸出JSON數據:\n"); console.log(JSON.stringify(this.data.arrM)); } }); //(1) 相關的概念 Task(任務-hi) | target(目標) //(2) 任務的配置:任務中可以配置一個或者是多個目標 調用config //(3) 復合任務的執行(多任務-多target) // 001 grunt TaskName 把當前Task下面所有的目標操作都執行一遍 // 002 grunt TaskName:targetName 執行當前Task下面的某一個指定的目標 grunt.registerTask("default",["hi"]); };
具體的執行情況
wendingding:06-Grunt項目多任務和options wendingding$ grunt Running "hi:targetA" (hi) task 任務當前執行的target: targetA 任務當前執行的target對應的數據: 輸出數組: [ 'targetA_1', 'targetA_2', 'targetA_3' ] Running "hi:targetB" (hi) task 任務當前執行的target: targetB 任務當前執行的target對應的數據: 輸出JSON數據: ["targetB_1","targetB_2","targetB_3"] Running "hi:targetC" (hi) task 任務當前執行的target: targetC 任務當前執行的target對應的數據: 輸出數組: [ 'targetC_1', 'targetC_2', 'targetC_3' ] Done
代碼說明
上面的代碼中定義了一個多目標任務,Task的名稱為hi,該Task有三個target目標選項,分別是targetA、targetB和targetC。在任務配置相關代碼中,全局的options配置項中outPut屬性對應的值為array,表示具體的目標任務在執行的時候以數組的形式輸出。
我們看到在targetB目標中重寫了options選項中的outPut屬性為json,當終端執行$ grunt命令的時候,會依次執行所有三個target目標選項,而targetA和targetC以數組格式來輸出內容,targetB則以json格式來輸出內容。
Grunt多目標任務以及選項使得我們可以針對不同的應用環境,以不同的方式來運行同一個Task。可以利用這一點,我們完全能夠定義Task為不同的構建環境創建不同的輸出目標。
說明 ? this.options()方法用于獲取當前正在執行的目標Task的options配置選項
Grunt項目中配置模板的簡單使用
在Grunt項目中,我們可以使用<% %>分隔符的方式來指定模板,當Task讀取自己配置信息的時候模板的具體內容會自動擴展,且支持以遞歸的方式展開。
在通過<%=... %>在向模板綁定數據的時候,我們可以直接傳遞配置對象中的屬性或調用grunt提供的方法,模板中屬性的上下文就是當前的配置對象。
下面,我們通過Gruntfile文件中的一段核心代碼來展現配置模板的使用情況。
module.exports=function (grunt) { //(1) 創建并設置grunt的配置對象 //配置對象:該對象將作為參數傳遞給grunt.config.init方法 var configObj={ concat: { target: { //src:["src/demo1.js","src/demo2.js"] src: ['<%=srcPath %>demo1.js', '<%=srcPath %>demo2.js'], //dest:["dist/2018_05_21_index.js"] dest: '<%=targetPath %>', }, }, srcPath:"src/", destPath:"dist/", targetPath:"<%=destPath %><%=grunt.template.today('yyyy_mm_dd_') %>index.js" }; //(2) 調用init方法對任務(Task)進行配置 // grunt.config.init 方法===grunt.initConfig方法 grunt.config.init(configObj); //(3) 加載concat插件 grunt.loadNpmTasks("grunt-contrib-concat"); //(4) 注冊Task grunt.registerTask("default",["concat"]); };
上面這段代碼對concat插件代碼合并Task進行了配置,使用到了模板技術。該任務把src目錄下的demo1和demo2兩個js文件合并到dist目錄下并命名為2018_05_21_index.js文件。
Grunt項目中導入外部的數據
在向模板綁定數據的時候,常見的做法還會導入外部的數據,并把導入的數據設置為配置對象的指定屬性值。比如在開發中常常需要用到當前Grunt項目的元信息,包括名稱、版本等,這些數據常通過調用grunt.file.readJSON方法加載package.json文件的方式獲取。下面給出代碼示例:
//包裝函數 module.exports=function (grunt) { //設置(demoTask和concat)Task的配置信息 grunt.config.init({ //從package.json文件中讀取項目的元(基本)信息 pkg:grunt.file.readJSON("package.json"), //demoTask的配置信息 demoTask :{ banner:"<%=pkg.name%> -- <%=pkg.version%>" }, //concat的配置信息 concat:{ options:{ stripBanners:true, banner:'/*項目名稱:<%=pkg.name%> 項目版本:<%=pkg.version%> 項目的作者:<%=pkg.author%> 更新時間:<%=grunt.template.today("yyyy-mm-dd")%>*/\n' }, target:{ src:["src/demo1.js","src/demo2.js"], dest:'dist/index.js' } } }); //自定義Task 任務的名稱為demoTask grunt.registerMultiTask("demoTask",function () { console.log("執行demo任務"); //表示調用config方法來讀取demoTask里面的banner屬性并輸出 console.log(grunt.config("demoTask.banner")); }); //從node_modules目錄中加載concat插件 //注意:需要先把插件下載到本地 npm install grunt-contrib-concat --save-dev grunt.loadNpmTasks("grunt-contrib-concat"); //注冊任務 grunt.registerTask("default",["demoTask","concat"]); };
如果在終端輸入$ grunt命令執行,那么demoTask任務將會輸出grunt_demo -- 1.0.0打印消息,而concat任務則把兩個js文件合并到dist目錄下面的index.js文件并添加注釋信息。
wendingding$ grunt Running "demoTask:banner" (demoTask) task 執行demo任務 grunt_demo -- 1.0.0 Running "concat:target" (concat) task Done. wendingding:07-Grunt項目模板配置 wendingding$ cat dist/index.js /*項目名稱:grunt_demo 項目版本:1.0.0 項目的作者:文頂頂 更新時間:2018-05-21*/ console.log("demo1"); console.log("demo2");
說明 grunt.file.readJSON方法用于加載JSON數據,grunt.file.readYAML方法用于加載YAML數據。
到這里,基本上就可以說已經熟練掌握Grunt了。上文我們在進行代碼演示的時候,不論是自定義任務還是Grunt插件使用的講解都是片段性的,支離破碎的,Grunt作為一款自動化構建工具,自動化這三個字到現在還沒有體現出來。
顧名思義,自動化構建的意思就是能夠監聽項目中指定的文件,當這些文件發生改變后自動的來執行某些特定的任務。 否則的話,每次修改文件后,都需要我們在終端里面輸入對應的命令來重新執行,這頂多能算半自動化是遠遠不夠的。
下面給出一份更全面些的Gruntfile文件,該文件中使用了幾款常用的Grunt插件(uglify、cssmin、concat等)來搭建自動化構建項目的工作流。點擊獲取演示代碼
//包裝函數 module.exports=function (grunt) { // 項目配置信息 grunt.config.init({ pkg:grunt.file.readJSON("package.json"), //代碼合并 concat:{ options:{ stripBanners:true, banner:'/*項目名稱:<%=pkg.name%> 項目版本:<%=pkg.version%> 項目的作者:<%=pkg.author%>' +' 更新時間:<%=grunt.template.today("yyyy-mm-dd")%>*/\n' }, target:{ src:["src/demo1.js","src/demo2.js"], dest:'dist/index.js' } }, //js代碼壓縮 uglify:{ target:{ src:"dist/index.js", dest:"dist/index.min.js" } }, //css代碼壓縮 cssmin:{ target:{ src:"src/index.css", dest:"dist/index.min.css" } }, //js語法檢查 jshint:{ target:['Gruntfile.js',"dist/index.js"], options:{ jshintrc:".jshintrc" } }, //監聽 自動構建 watch:{ target:{ files:["src/*.js","src/*.css"], //只要指定路徑的文件(js和css)發生了變化,就自動執行tasks中列出的任務 tasks:["concat","jshint","uglify","cssmin"] } } }); //通過命令行安裝插件(省略...) //從node_modules路徑加載插件 grunt.loadNpmTasks("grunt-contrib-concat"); grunt.loadNpmTasks("grunt-contrib-uglify"); grunt.loadNpmTasks("grunt-contrib-cssmin"); grunt.loadNpmTasks("grunt-contrib-jshint"); grunt.loadNpmTasks("grunt-contrib-watch"); //注冊任務:在執行$ grunt命令的時候依次執行代碼的合并|檢查|壓縮等任務并開啟監聽 grunt.registerTask("default",["concat","jshint","uglify","cssmin","watch"]) };
當在終端輸入$ grunt命令的時候,grunt會執行以下任務
① 合并src/demo1.js和src/demo2.js文件并命名為index.js保存到dist目錄
② 按照既定的規則對Gruntfile.js和index.js文件來進行語法檢查
③ 壓縮index.js文件并命名為index.min.js保存在dist目錄
④ 壓縮src/index.css文件并保存到dist/index.min.css
⑤ 開啟監聽,如果src目錄下面的js文件或css文件被更改則重新構建
作者:叩丁狼教育前端學科-楊勇老師
avaScript的框架、庫和工具的冒出似乎有點超出大家的想象,截止到2017年5月,在GitHub上搜索JavaScript項目,你會發現其已經超過了110萬;npmjs.org上有50萬個可用的軟件包,每月下載量近100億次。
為了幫助大家更好地選擇JavaScript框架、庫和工具,本文將對流行的框架、庫和工具進行一些對比,但是由于篇幅有限,可能并不能包含到所有的框架、庫和工具,所以歡迎大家在下方補充評論,共同學習進步。
為了讓大家的討論在共同的水平線上,首先我們先來確定一下框架、庫和工具的概念。可能每個人對于這三者都有自己的理解,但是本文是基于以下的概念來進行討論的。
庫
庫是有用功能的有組織的集合。庫的典型功能包括處理字符串、日期、HTML DOM元素、事件、Cookie、動畫、網絡請求等。每個函數將值返回給調用應用程序,但是你可以從中選擇參數來應用。如果用汽車來做比喻,那就是你可以任意使用所有的零部件來搭建汽車,但是你必須自行構建引擎。
庫通常是提供一個更高的抽象層,平滑的實現細節和矛盾。例如,Ajax通常依賴于XMLHttpRequest API,但是由于各瀏覽器之間的差異,你可能需要修改幾行代碼來實現。但是庫可以提供一個更簡單的ajax()函數,讓程序員更專注于高層次的業務邏輯。
因為庫不必在意更多的細節,所以開發時間可能會縮短20%,但是它也不是沒有缺點的:
庫內的錯誤可能難以定位和修復
開發團隊不能保證快速發布補丁
修補程序可能會更改API,并對您的代碼進行重大更改。
框架
框架是一個應用程序的骨架,它要求你以特定的方式處理軟件設計,并在某些點插入自己的邏輯。 通常框架提供事件、存儲和數據綁定等功能。 如果我們還是用汽車了來做類比,那么框架就是一輛車的底盤、車身和發動機,為了讓車輛始終保持運行狀態,你可以添加、刪除或修改某些組件。
框架通常會提供比庫更高的抽象層,并且幫助用戶快速構建項目的08%,但它的缺點是:
如果應用程序超出了框架的范圍,那么剩下的20%可能會很難完成;
框架更新可能很困難 ;
框架核心代碼和概念很少更新,但是同樣的事情,程序員往往都會在短時間內發現一個更好的解決方式;
工具
工具有助于開發,但并不是項目的組成部分。 工具包括系統構建,編譯器, transpilers,代碼分割器,圖像壓縮器等。
工具的應用使得開發過程變得更加容易,例如很多程序員都喜歡將Sass to CSS,因為它提供了代碼分離,嵌套,渲染時間變量,循環和函數。 瀏覽器不了解Sass / SCSS語法,因此在測試和部署之前,必須使用適當的工具將代碼編譯為CSS。
JavaScript框架和庫
jQuery
jQuery是最常用的JavaScript庫,它革命性的在客戶端開發,將CSS選擇器引入到DOM節點檢索加鏈接來應用事件處理程序、動畫和Ajax調用。jQuery近年來備受青睞,對于一個很需要JavaScript功能的項目來說,jQuery絕對是一個可行的選擇。
優點:
分布規模小;
學習曲線平緩,在線幫助多;
語法簡潔;
容易延伸;
缺點:
增加了本機API的速度開銷
瀏覽器兼容性的改善降低了它的重要性;
用法扁平
有些行業反饋有很多不必要的使用。
React
React可能是去年一年最受關注的庫了吧。React聲稱是一個用于構建用戶界面的JavaScript庫,它專注于MVC開發的“View”部分,并且可以輕松創建保留狀態的UI組件。 它是實現虛擬DOM的第一個庫, 內存結構計算差異,有效地更新頁面。
從使用情況來看,React的情況似乎有些不好,但這是因為它是在應用程序中使用而不是網站,38%的程序員表示他們正在使用該庫。
優點:
小巧,高效,快捷靈活;
簡單的組件模型;
良好的文檔和在線資源;
服務器端渲染;
處于高速發展期;
缺點:
需要學習新的概念和語法;
構建工具必不可少;
要求其他庫或框架提供Model和Control;
與修改DOM的代碼和其他庫不兼容;
Lodash and Underscore
Lodash和Underscore提供了數百個功能性的JavaScript實用程序來補充本地字符串,數字,數組和其他原始對象方法。 它在客戶端使用率較低,但是可以在服務器端的Node.js應用程序中使用很頻繁。
優點:
小而簡單;
擁有優質文檔,易于學習;
與大多數庫和框架兼容;
不擴展內置對象;
可以在客戶端或服務器上使用;
缺點:
有些方法只適用于ES2015及更高版本的JavaScript。
AngularJS 1.x
Angular最流行的版本是1.x版本,它使用雙向數據綁定擴展HTML,同時將DOM操作與應用程序邏輯脫鉤。盡管版本2已經發布(當然現在已經到了版本4),但是Angular 1.x仍在開發中。
優點:
眾多大公司采用;
以單一的解決方案來生產現代Web應用程序;
一個解決方案來生產現代Web應用程序;
MEAN堆棧(MongoDB,Express.JS,AngularJS,NodeJS),有眾多文檔和教程可用來參考;
缺點:
學習曲線更加陡峭;
大代碼庫
不能升級到Angular 2.x
Angular 2.x (now 4.x)
Angular 2.0于2016年9月發布。這是一個完整的重寫,它引入了使用TypeScript(被編譯為JavaScript)創建的基于模塊化組件的模型。 Angular 4.0版本于2017年3月發布。
Angular2+和1.0版本截然不同,與其他也不兼容,所以也許谷歌應該給該項目另外起一個名字。
優點:
單一的解決方案來生產現代Web應用程序;
盡管Angular 2+的可用文檔較少,但它仍是MEAN堆棧的一部分;
對于熟悉靜態類型語言(如C#和Java)的人員,TypeScript提供了一些優勢。
缺點:
更陡峭的學習曲線;
大代碼庫;
不能從Angular 1.x升級;
與1.x相比,Angular 2.x的使用率相對較低;
盡管是Google的項目,但Google似乎并沒有使用它?
Vue.js
Vue.js是一個用于構建用戶界面的輕量級漸進框架。 該核心提供了一個React-like 的虛擬 DOM-powered層,它可以與其他庫集成,也可以支持單頁應用程序。
Vue.js使用HTML模板語法將DOM綁定到實例數據。 模型是在更改數據時更新視圖的純JavaScript對象。 附加工具提供了scaffolding,路由,狀態管理,動畫等功能。
優點:
易于上手,普及度高;
起點簡單,但完成滿意度高;
依賴性小,性能好;
缺點:
是一個新項目,所以風險可能會很大;
依賴開發人員來更新;
相對同類框架,資源較少;
Backbone.js
Backbone.js是提供常見的服務器端框架MVC結構最早的客戶端選項之一,它唯一的依賴是由同一開發人員創建的Underscore.js。
Backbone.js聲稱是一個庫,因為它可以與其他項目集成,但我認為大多數程序員都認為它是一個框架。
優點:
體積小,重量輕,復雜度低;
不添加HTML的邏輯;
文檔豐富;
采用了許多應用,包括Trello,WordPress.com,LinkedIn和Groupon;
缺點:
與AngularJS等相比,抽象度較低;
需要額外的組件來實現數據綁定等功能;
新的框架基本已經不再采用MVC架構;
Ember.js
Ember.js是基于Model-View-ViewModel(MVVM)模式的框架之一。 它在單個包中實現模板化,數據綁定和庫。如果 Ruby on Rails體驗的用戶,能夠迅速熟悉其配置概念。
優點:
為客戶端應用程序提供單一解決方案;
程序員可以快速開發—其使用jQuery;
良好的向后兼容性和升級選項;
采用了現代Web開發標準;
缺點:
與其他正在向較小組件結構移動的框架相比,被認為是單一的;
陡峭的學習曲線 ;
Knockout.js
較早的MVVM框架之一,Knockout.js使用觀察者來確保UI與底層數據保持同步,它具有模板和依賴關系跟蹤。
優點:
小而輕便,無依賴
支持回溯到IE6
優質文檔;
缺點:
較大的項目可能變得復雜;
發展速度已經放緩;
使用情況正在下降;
值得注意,下面這些項目雖然不如上面的受歡迎,但還是值得一試的:
Polymer- 可以跨瀏覽器支持HTML5網頁組件的庫
Meteor - 一個用于Web應用程序的全棧平臺。
Aurelia - 一種相對較新的,輕量級的跨平臺框架
Svelte - 一個將框架源代碼轉換為JavaScript的新項目
Conditioner.js - 一個基于狀態自動加載和卸載模塊的庫。
工具:General-Purpose Task Runners
構建工具可以自動執行各種Web開發任務,例如預處理,編譯,優化圖像,縮小代碼,運行測試等等。所有的任務都可以在一個可執行包中管理,比較受歡迎的工具包括:
Gulp.js
Gulp雖然不是第一個工具,但是它是最受歡迎的工具,Gulp使用易于閱讀的JavaScript代碼,將源文件加載到流中,并在將數據輸出到構建文件夾之前通過各種插件管理數據。
npm
npm是Node.js包管理器,但其腳本工具可用于運行通用任務。 對于具有很少依賴關系的簡單項目來說,這是一個有吸引力的選擇,但是對于復雜的任務來說,它可能就有些有心無力。
Grunt
Grunt是第一個實現批量采用的JavaScript任務的工具,但其速度和復雜的JSON配置,使得Gulp異軍突起。如今,這些問題解決了,Grunt仍然是一個不錯的選擇。
工具:Module Bundlers
多個JavaScript文件的管理成為了程序員們的煩惱,在默認情況下,瀏覽器文件未被編譯,因此依賴關系必須以適當的順序加載或連接。雖然有各種選項,如ES6模塊和CommonJS,但瀏覽器支持畢竟是有限的,因此Module Bundlers就變得至關重要。
Webpack
Webpack支持所有流行的模塊選項,并已成為React開發的代名詞。 雖然它聲稱是一個Module Bundlers,但是也可以用作通用任務運行程序。
Browserify
Browserify支持Node.js使用的CommonJS模塊,將所有模塊編譯成單個瀏覽器兼容的文件。
RequireJS
RequireJS是一種瀏覽器中的模塊加載器,它也可以在Node.js中使用。
Tools: Linting
“Linting”是分析你的代碼的潛在錯誤或偏離語法標準。 有了這種工具,你永遠不出現只有一半括號或者未聲明變量的情況。
ESLint
ESLint是一種可插拔的Linting工具,每個規則都是一個插件,因此可以根據您的喜好進行配置。
JSHint
一個靈活的JavaScript linter,在真正的錯誤和迂腐的語法需求之間取得了很好的平衡!
JSLint
JSLint是最早的Linter之一,遵循一套嚴格的默認規則。
Tools: Test Suites
在應用程序的編寫過程中有一個很重要的步驟那就是代碼測試。代碼測試的工具有很多,如Ava、Tape和Jest。下面,我們就為大家介紹最受歡迎的三個選擇:
Mocha
Mocha是一個JavaScript測試框架,可以在Node.js或瀏覽器中運行測試。 它支持異步測試,并且經常與Chai配對,以使測試代碼能夠以可讀取的方式表達。
Jasmine
Jasmine是一個行為驅動的測試套件,可以在瀏覽器中自動測試您的UI和交互。
QUnit
QUnit是一個單元測試框架,可以通過特定參數檢查函數結果。
Tools: Miscellaneous
雖然JavaScript比較常見常用,但是也并不是每個程序員都喜歡JavaScript,例如TypeScript,LiveScript和CoffeeScrip這些也可以使得程序員的開發過程很愉快。
JavaScript-powered HTML的引擎模板有數十種,其中包括Mustache,Handlebars,Pug(Jade)和EJS。但在我而言, 更喜歡保留JavaScript語法(如EJS和doT)的輕量級選項。
如何自己來編寫文檔呢?ES2015兼容的文檔生成器包括ESDoc,JSDoc,YUIdoc,documentation.js和Transcription。
寫在最后
如果你想要走在技術的前端,那么React以及和其相關的技術發展方向值得關注。如果你想要為Web應用程序選擇一個安全的選項,那么你可以考慮Vue.js。
雖然整體框架現在不再那么受歡迎,但是如果你是要做嚴格的大型項目結構,AngularJS會是一個不錯的選擇。雖然,現在大多數人還在使用1.0版本,但是從長遠來看,學習一下TypeScript,選擇4.0版本會更加安全。
jQuery雖然在技術新聞中很少被提到,但是它的學習曲線平緩,幾乎所有的程序員都可以理解,而且它現在還在積極開發。
工具的選擇會因項目而異,但是不可否認,大多數項目都會選擇Gulp和WebPack。每個項目和團隊的技能都是不同的,所以你在選擇的時候要在有限時間內準確評估。
最后,永遠不要忘記庫,框架和工具是可選的! JavaScript在過去的十年中發生了革命性的變化,幾乎每隔幾個月都會有熱門框架的出現,所以很容易就掉進陷阱之中。所以,在選擇時,就要考慮自己的實際需求,也要積極學習新的知識。
avaScript的框架、庫和工具的冒出似乎有點超出大家的想象,截止到2017年5月,在GitHub上搜索JavaScript項目,你會發現其已經超過了110萬;npmjs.org上有50萬個可用的軟件包,每月下載量近100億次。
為了幫助大家更好地選擇JavaScript框架、庫和工具,本文將對流行的框架、庫和工具進行一些對比,但是由于篇幅有限,可能并不能包含到所有的框架、庫和工具,所以歡迎大家在下方補充評論,共同學習進步。
為了讓大家的討論在共同的水平線上,首先我們先來確定一下框架、庫和工具的概念。可能每個人對于這三者都有自己的理解,但是本文是基于以下的概念來進行討論的。
庫
庫是有用功能的有組織的集合。庫的典型功能包括處理字符串、日期、HTML DOM元素、事件、Cookie、動畫、網絡請求等。每個函數將值返回給調用應用程序,但是你可以從中選擇參數來應用。如果用汽車來做比喻,那就是你可以任意使用所有的零部件來搭建汽車,但是你必須自行構建引擎。
庫通常是提供一個更高的抽象層,平滑的實現細節和矛盾。例如,Ajax通常依賴于XMLHttpRequest API,但是由于各瀏覽器之間的差異,你可能需要修改幾行代碼來實現。但是庫可以提供一個更簡單的ajax()函數,讓程序員更專注于高層次的業務邏輯。
因為庫不必在意更多的細節,所以開發時間可能會縮短20%,但是它也不是沒有缺點的:
庫內的錯誤可能難以定位和修復
開發團隊不能保證快速發布補丁
修補程序可能會更改API,并對您的代碼進行重大更改。
框架
框架是一個應用程序的骨架,它要求你以特定的方式處理軟件設計,并在某些點插入自己的邏輯。 通常框架提供事件、存儲和數據綁定等功能。 如果我們還是用汽車了來做類比,那么框架就是一輛車的底盤、車身和發動機,為了讓車輛始終保持運行狀態,你可以添加、刪除或修改某些組件。
框架通常會提供比庫更高的抽象層,并且幫助用戶快速構建項目的08%,但它的缺點是:
如果應用程序超出了框架的范圍,那么剩下的20%可能會很難完成;
框架更新可能很困難 ;
框架核心代碼和概念很少更新,但是同樣的事情,程序員往往都會在短時間內發現一個更好的解決方式;
工具
工具有助于開發,但并不是項目的組成部分。 工具包括系統構建,編譯器, transpilers,代碼分割器,圖像壓縮器等。
工具的應用使得開發過程變得更加容易,例如很多程序員都喜歡將Sass to CSS,因為它提供了代碼分離,嵌套,渲染時間變量,循環和函數。 瀏覽器不了解Sass / SCSS語法,因此在測試和部署之前,必須使用適當的工具將代碼編譯為CSS。
JavaScript框架和庫
jQuery
jQuery是最常用的JavaScript庫,它革命性的在客戶端開發,將CSS選擇器引入到DOM節點檢索加鏈接來應用事件處理程序、動畫和Ajax調用。jQuery近年來備受青睞,對于一個很需要JavaScript功能的項目來說,jQuery絕對是一個可行的選擇。
優點:
分布規模小;
學習曲線平緩,在線幫助多;
語法簡潔;
容易延伸;
缺點:
增加了本機API的速度開銷
瀏覽器兼容性的改善降低了它的重要性;
用法扁平
有些行業反饋有很多不必要的使用。
React
React可能是去年一年最受關注的庫了吧。React聲稱是一個用于構建用戶界面的JavaScript庫,它專注于MVC開發的“View”部分,并且可以輕松創建保留狀態的UI組件。 它是實現虛擬DOM的第一個庫, 內存結構計算差異,有效地更新頁面。
從使用情況來看,React的情況似乎有些不好,但這是因為它是在應用程序中使用而不是網站,38%的程序員表示他們正在使用該庫。
優點:
小巧,高效,快捷靈活;
簡單的組件模型;
良好的文檔和在線資源;
服務器端渲染;
處于高速發展期;
缺點:
需要學習新的概念和語法;
構建工具必不可少;
要求其他庫或框架提供Model和Control;
與修改DOM的代碼和其他庫不兼容;
Lodash and Underscore
Lodash和Underscore提供了數百個功能性的JavaScript實用程序來補充本地字符串,數字,數組和其他原始對象方法。 它在客戶端使用率較低,但是可以在服務器端的Node.js應用程序中使用很頻繁。
優點:
小而簡單;
擁有優質文檔,易于學習;
與大多數庫和框架兼容;
不擴展內置對象;
可以在客戶端或服務器上使用;
缺點:
有些方法只適用于ES2015及更高版本的JavaScript。
AngularJS 1.x
Angular最流行的版本是1.x版本,它使用雙向數據綁定擴展HTML,同時將DOM操作與應用程序邏輯脫鉤。盡管版本2已經發布(當然現在已經到了版本4),但是Angular 1.x仍在開發中。
優點:
眾多大公司采用;
以單一的解決方案來生產現代Web應用程序;
一個解決方案來生產現代Web應用程序;
MEAN堆棧(MongoDB,Express.JS,AngularJS,NodeJS),有眾多文檔和教程可用來參考;
缺點:
學習曲線更加陡峭;
大代碼庫
不能升級到Angular 2.x
Angular 2.x (now 4.x)
Angular 2.0于2016年9月發布。這是一個完整的重寫,它引入了使用TypeScript(被編譯為JavaScript)創建的基于模塊化組件的模型。 Angular 4.0版本于2017年3月發布。
Angular2+和1.0版本截然不同,與其他也不兼容,所以也許谷歌應該給該項目另外起一個名字。
優點:
單一的解決方案來生產現代Web應用程序;
盡管Angular 2+的可用文檔較少,但它仍是MEAN堆棧的一部分;
對于熟悉靜態類型語言(如C#和Java)的人員,TypeScript提供了一些優勢。
缺點:
更陡峭的學習曲線;
大代碼庫;
不能從Angular 1.x升級;
與1.x相比,Angular 2.x的使用率相對較低;
盡管是Google的項目,但Google似乎并沒有使用它?
Vue.js
Vue.js是一個用于構建用戶界面的輕量級漸進框架。 該核心提供了一個React-like 的虛擬 DOM-powered層,它可以與其他庫集成,也可以支持單頁應用程序。
Vue.js使用HTML模板語法將DOM綁定到實例數據。 模型是在更改數據時更新視圖的純JavaScript對象。 附加工具提供了scaffolding,路由,狀態管理,動畫等功能。
優點:
易于上手,普及度高;
起點簡單,但完成滿意度高;
依賴性小,性能好;
缺點:
是一個新項目,所以風險可能會很大;
依賴開發人員來更新;
相對同類框架,資源較少;
Backbone.js
Backbone.js是提供常見的服務器端框架MVC結構最早的客戶端選項之一,它唯一的依賴是由同一開發人員創建的Underscore.js。
Backbone.js聲稱是一個庫,因為它可以與其他項目集成,但我認為大多數程序員都認為它是一個框架。
優點:
體積小,重量輕,復雜度低;
不添加HTML的邏輯;
文檔豐富;
采用了許多應用,包括Trello,WordPress.com,LinkedIn和Groupon;
缺點:
與AngularJS等相比,抽象度較低;
需要額外的組件來實現數據綁定等功能;
新的框架基本已經不再采用MVC架構;
Ember.js
Ember.js是基于Model-View-ViewModel(MVVM)模式的框架之一。 它在單個包中實現模板化,數據綁定和庫。如果 Ruby on Rails體驗的用戶,能夠迅速熟悉其配置概念。
優點:
為客戶端應用程序提供單一解決方案;
程序員可以快速開發—其使用jQuery;
良好的向后兼容性和升級選項;
采用了現代Web開發標準;
缺點:
與其他正在向較小組件結構移動的框架相比,被認為是單一的;
陡峭的學習曲線 ;
Knockout.js
較早的MVVM框架之一,Knockout.js使用觀察者來確保UI與底層數據保持同步,它具有模板和依賴關系跟蹤。
優點:
小而輕便,無依賴
支持回溯到IE6
優質文檔;
缺點:
較大的項目可能變得復雜;
發展速度已經放緩;
使用情況正在下降;
值得注意,下面這些項目雖然不如上面的受歡迎,但還是值得一試的:
Polymer- 可以跨瀏覽器支持HTML5網頁組件的庫
Meteor - 一個用于Web應用程序的全棧平臺。
Aurelia - 一種相對較新的,輕量級的跨平臺框架
Svelte - 一個將框架源代碼轉換為JavaScript的新項目
Conditioner.js - 一個基于狀態自動加載和卸載模塊的庫。
工具:General-Purpose Task Runners
構建工具可以自動執行各種Web開發任務,例如預處理,編譯,優化圖像,縮小代碼,運行測試等等。所有的任務都可以在一個可執行包中管理,比較受歡迎的工具包括:
Gulp.js
Gulp雖然不是第一個工具,但是它是最受歡迎的工具,Gulp使用易于閱讀的JavaScript代碼,將源文件加載到流中,并在將數據輸出到構建文件夾之前通過各種插件管理數據。
npm
npm是Node.js包管理器,但其腳本工具可用于運行通用任務。 對于具有很少依賴關系的簡單項目來說,這是一個有吸引力的選擇,但是對于復雜的任務來說,它可能就有些有心無力。
Grunt
Grunt是第一個實現批量采用的JavaScript任務的工具,但其速度和復雜的JSON配置,使得Gulp異軍突起。如今,這些問題解決了,Grunt仍然是一個不錯的選擇。
工具:Module Bundlers
多個JavaScript文件的管理成為了程序員們的煩惱,在默認情況下,瀏覽器文件未被編譯,因此依賴關系必須以適當的順序加載或連接。雖然有各種選項,如ES6模塊和CommonJS,但瀏覽器支持畢竟是有限的,因此Module Bundlers就變得至關重要。
Webpack
Webpack支持所有流行的模塊選項,并已成為React開發的代名詞。 雖然它聲稱是一個Module Bundlers,但是也可以用作通用任務運行程序。
Browserify
Browserify支持Node.js使用的CommonJS模塊,將所有模塊編譯成單個瀏覽器兼容的文件。
RequireJS
RequireJS是一種瀏覽器中的模塊加載器,它也可以在Node.js中使用。
Tools: Linting
“Linting”是分析你的代碼的潛在錯誤或偏離語法標準。 有了這種工具,你永遠不出現只有一半括號或者未聲明變量的情況。
ESLint
ESLint是一種可插拔的Linting工具,每個規則都是一個插件,因此可以根據您的喜好進行配置。
JSHint
一個靈活的JavaScript linter,在真正的錯誤和迂腐的語法需求之間取得了很好的平衡!
JSLint
JSLint是最早的Linter之一,遵循一套嚴格的默認規則。
Tools: Test Suites
在應用程序的編寫過程中有一個很重要的步驟那就是代碼測試。代碼測試的工具有很多,如Ava、Tape和Jest。下面,我們就為大家介紹最受歡迎的三個選擇:
Mocha
Mocha是一個JavaScript測試框架,可以在Node.js或瀏覽器中運行測試。 它支持異步測試,并且經常與Chai配對,以使測試代碼能夠以可讀取的方式表達。
Jasmine
Jasmine是一個行為驅動的測試套件,可以在瀏覽器中自動測試您的UI和交互。
QUnit
QUnit是一個單元測試框架,可以通過特定參數檢查函數結果。
Tools: Miscellaneous
雖然JavaScript比較常見常用,但是也并不是每個程序員都喜歡JavaScript,例如TypeScript,LiveScript和CoffeeScrip這些也可以使得程序員的開發過程很愉快。
JavaScript-powered HTML的引擎模板有數十種,其中包括Mustache,Handlebars,Pug(Jade)和EJS。但在我而言, 更喜歡保留JavaScript語法(如EJS和doT)的輕量級選項。
如何自己來編寫文檔呢?ES2015兼容的文檔生成器包括ESDoc,JSDoc,YUIdoc,documentation.js和Transcription。
寫在最后
如果你想要走在技術的前端,那么React以及和其相關的技術發展方向值得關注。如果你想要為Web應用程序選擇一個安全的選項,那么你可以考慮Vue.js。
雖然整體框架現在不再那么受歡迎,但是如果你是要做嚴格的大型項目結構,AngularJS會是一個不錯的選擇。雖然,現在大多數人還在使用1.0版本,但是從長遠來看,學習一下TypeScript,選擇4.0版本會更加安全。
jQuery雖然在技術新聞中很少被提到,但是它的學習曲線平緩,幾乎所有的程序員都可以理解,而且它現在還在積極開發。
工具的選擇會因項目而異,但是不可否認,大多數項目都會選擇Gulp和WebPack。每個項目和團隊的技能都是不同的,所以你在選擇的時候要在有限時間內準確評估。
最后,永遠不要忘記庫,框架和工具是可選的! JavaScript在過去的十年中發生了革命性的變化,幾乎每隔幾個月都會有熱門框架的出現,所以很容易就掉進陷阱之中。所以,在選擇時,就要考慮自己的實際需求,也要積極學習新的知識。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。