原文連接:https://medium.freecodecamp.com/why-i-left-gulp-and-grunt-for-npm-scripts-3d6853dd22b8#.n7m1855ufjavascript
做者:Cory House前端
------------------------------------------------------------------------------------------------------------------------------java
我知道你在想什麼。 什麼?!Gulp不是纔剛把Grunt幹掉嗎? 爲何咱們不能在Javascript的地盤上消停一下子? 我據說過, 可是。。。node
我發現Gulp和Grunt都是些沒必要要的抽象。npm腳本已經足夠強大並且每每更容易忍受。react
讓咱們從一個例子開始webpack
我是一個Gulp的超級粉絲。可是在個人上個項目中, 最終個人gulp文件中有幾百行代碼和一大堆Gulp插件。我曾經爲了用Gulp整合這堆東西而掙扎不已,有Webpack、Browsersync、hot reloading、Mocha等等。爲何? 好吧,有些插件的文檔沒覆蓋到個人使用場景。有些插件只公開了部分我須要的API。有個插件有一個奇怪的bug,它能監視的文件數很小。另外一個插件則在命令行中輸出信息時掉色了。git
這些都是能夠解決的問題,可是當我直接使用這些工具時從不會發生這些問題。es6
最近我注意到不少開源項目都僅僅使用npm腳本。我決定退一步從新審視本身。 我真的須要Gulp嗎? 原來並非。github
我決定在個人新開源項目中嘗試只使用npm腳本。我建立了一個富開發環境,並僅用npm腳本爲React應用建立了一個進程。想知道這看起來像什麼嗎? 你能夠查看React彈弓項目。 在Pluralsight平臺(一家美國軟件開發在線教育平臺)上我解決了用npm腳本爲「用ES6編寫React和Redux」這個目標建立進程的問題。web
讓我驚喜的是,相比Gulp,如今我更樂於使用npm腳本。這就是緣由。
Gulp和Grunt作錯什麼了?
隨着時間的推移,我注意到像Gulp和Grunt這樣的任務執行器都具備的三個核心問題:
1. 依賴插件製做者
2. 調試困難
3. 滯後的文檔
讓咱們依次思考這三個問題。
問題#1: 依賴插件製做者
當你使用一個新的或不流行的技術時,可能根本沒有對應的插件。 當有插件出現時,這個技術可能已通過時了。 舉個例子, Babel6剛剛發佈不久。 它的API變化很大,不少Gulp插件不兼容這個最新版本。當使用Gulp時,我曾由於我須要的Gulp插件還沒更新而被卡住了。
使用Gulp或者Grunt的話,你必需要等插件維護者提供更新,或者你本身來修復這些問題。這會延緩你應用最新的現代化工具的能力。相對的,當我使用npm腳本時,我直接使用工具,而不須要一個額外的抽象層。這表示當Mocha、Istanbul、Babel、Webpack、Browserify等工具的最新版本發佈時,我有辦法立刻應用這些最新的版本。
在選擇方面,沒什麼能夠戰勝npm:
當你使用npm腳本時,你不須要搜索任何Grunt或Gulp插件。你直接在超過227,000個npm包中選擇便可。
公平起見,若是你須要的Grunt或Gulp插件變得不可用了,你固然能夠直接應用npm包。但今後你就再也不須要爲了那些特殊的任務而藉助Gulp或Grunt了。
問題#2: 調試困難
在集成失敗時,在Grunt和Gulp中進行調試會讓人很泄氣。 當你在一個額外的抽象層下工做的時候,可能引起bug的潛在緣由將更多:
1. 是否是基礎工具壞了?
2. 是否是Grunt/Gulp組件壞了?
3. 是否是個人配置錯了?
4. 是否是我用了不兼容的版本?
使用npm腳本能夠根除第二點. 我以爲第三點不多見,由於我一般是直接調用工具的命令行接口。 最後,自從我直接使用npm代替任務執行器的抽象概念後,我項目中包的數量減小了,第四點也不多見了。
問題#3:滯後的文檔
我須要的核心工具的文檔幾乎老是比相應的Grunt和Gulp組件的要好。例如, 若是我使用gulp-eslint,到頭來我須要將時間分別花在gulp-eslint文檔和ESLint網站上。 我不得不在抽象的組件和工具之間來回切換上下文環境。 Gulp和Grunt中的核心矛盾是這個:
只瞭解工具是遠遠不夠的。Gulp和Grunt還要求你理解插件的抽象概念。
大多數的工具提供清晰、強大和具有良好文檔的命令行接口。看看ESLint的CLI文檔就是一個很好的例子。我發如今npm腳本中讀取和實現一個簡短的命令行調用會更清晰、低衝突且更易於調試(在把那些抽象層移除後)。
如今我認爲我已經找到了痛點, 問題是, 爲何咱們以爲咱們須要像Gulp和Grunt那樣的任務執行器?
爲何咱們忽略了npm也能夠編譯?
我認爲有四個關鍵的誤解致使Gulp和Grunt變得如此流行:
1. 人們認爲使用npm腳本須要很高的命令行水平
2. 人們認爲npm腳本不夠強大
3. 人們認爲Gulp的流對快速構建來講是必須的
4. 人們認爲npm腳本不能跨平臺運行
讓咱們一個個解決這些誤解。
誤解#1:npm scripts須要很高的命令行水平
你不須要知道你的操做系統的不少命令行知識也能夠享用到npm腳本的力量。固然,grep、sed、awk和pipes命令是值得終身學習的技能,但你沒必要爲了使用npm腳本而成爲一個Unix或windows的命令行大師。 你能夠改成在npm中寫上千行書寫良好的腳原本完成你的工做。
例如,你可能不知道在Unix中強制刪除的命令行是:rm -rf。不要緊。 你可使用rimraf來完成相同的工做(並且它能夠跨平臺使用)。大多數npm包是在假設你對你的操做系統命令行知識知之甚少的前提下提供接口。當你要用某個功能的時候,只須要在npm上搜索你須要的包,閱讀文檔並學習便可。過去我老是搜索Gulp插件。如今我只搜索npm包。 有一個很是好的資源網站:libraries.io
誤解#2: npm scripts不夠強大
npm基本僅靠本身已經強大得使人驚訝。這些是常規的pre和post鉤子:
{ "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "prebuild": "echo I run before the build script", "build": "cross-env NODE_ENV=production webpack", "postbuild": "echo I run after the build script" } }
Github上的package.json連接
全部你要作的就是遵循約定。 以上腳本會基於他們的前綴按順序執行。 prebuild腳本會在build腳本以前執行,由於它有相同的名字,可是用了」pre「前綴。 postbuild腳本會在build腳本以後執行,由於它有」post「前綴。 因此, 若是我建立的腳本命名爲prebuild、build和postbuild, 當我敲入‘npm run build’命令時,它們會自動按順序執行。
你也能夠經過用一個腳本調用另外一個腳本的方法來將大問題分解:
{ "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "clean": "rimraf ./dist && mkdir dist", "prebuild": "npm run clean", "build": "cross-env NODE_ENV=production webpack" } }
Github上的package.json連接
在這個例子中,prebuild任務調用clean任務。這容許你將你的腳本分解得更小、命名更恰當、單一職責、一行內完成。
你能夠用&&操做符在一行命令中同步調用多個腳本。 在上述的clean步驟中的腳本會一個接一個執行。這種簡潔真的會讓你笑出聲,若是你曾爲了讓任務列表能在Gulp中按順序執行而苦苦掙扎過。
並且若是一個命令實在太複雜,你老是能夠調用一個單獨的文件:
{ "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "build": "node build.js" } }
Github上的package.json連接
以上代碼中,我正在build任務中調用一個單獨的腳本。這個腳本將會經過Node執行, 並且所以我能夠應用任何我須要的npm包, 和應用全部javascript中的功能。
我能夠繼續了, 但核心功能都記錄在這裏。還有,這裏也有一個短小的關於將npm做爲一個構建工具的Pluralsight課程. 或者,查看React彈弓項目並將其看成全部這些行爲的一個例子。
誤解#3: Gulp的流是快速構建所必需的
Gulp能夠從Grunt上快速取得市場主導權,是由於Gulp的內存流比Grunt的基於文件的作法快得多。可是你並不須要Gulp來享用流的強大。 事實上, 流功能早就已經集成到Unix和Windows的命令行中。 管道(|)操做符能夠將一個命令的輸出以流的形式做爲另外一個命令的輸入。 而重定向(>)操做符能夠將輸出重定向到一個文件中。
因此, 舉個例子, 在Unix中我可使用'grep'讀取一個文件的內容,並將其輸出重定向到一個新文件中:
grep ‘Cory House’ bigFile.txt > linesThatHaveMyName.txt
上面的作法就是流。沒有任何中間文件被建立。
(想知道上述命令在跨平臺的狀況下該怎麼作? 請讀下去。。。)
你一樣可使用'&'操做符在Unix上同時執行兩個命令:
npm run script1.js & npm run script2.js
以上這兩個腳本會在同時執行。爲了跨平臺的同時執行腳本,可使用npm-run-all。這就導向了下一個誤解。。。
誤解#4: npm腳本不能跨平臺運行
不少項目都依賴於特定操做系統,因此跨平臺的顧慮並不算重要。但若是你須要跨平臺運行,npm腳本依然能夠工做得很好。數不清的開源項目就是證據。接下來就是怎麼作了。
你的操做系統的命令行會運行你的npm腳本。 因此在Linux和OSX,你的npm腳本在一個Unix命令行上運行。 在Windows,npm腳本在Windows命令行上運行。所以,若是你想你的構建腳本能夠在全部平臺上運行,你須要同時讓Unix和Windows開心。 這裏有三個方法:
方法1: 使用跨平臺的命令。存在數量驚人的跨平臺命令。 這裏只是一小部分:
&& chain tasks (Run one task after another) < input file contents to a command > redirect command output to a file | redirect command output to another command
方法2: 使用node包。 你能夠用node包代替shell命令。例如, 使用rimraf代替'rm -rf‘. 使用cross-env在跨平臺方式下設置環境變量。 在google、npm或libraries.io中搜索你想要作什麼,幾乎確定會有一個node包能夠實現你的需求並且仍是跨平臺的。並且若是你的命令行調用已經太長,你能夠在單獨腳本中調用Node包,就像這樣:
node scriptName.js
上述腳本是普通的古老的javascript文件,經過Node執行。 而且由於你只是在命令行上調用一個腳本, 你不止能夠調用js文件。你能夠運行任何你的操做系統能執行的腳本, 例如Bash、Python、Ruby或Powershell等等。
方法3: 使用ShellJS。 ShellJS是一個npm包, 它能夠經過Node運行Unix命令。 這就給了你在任何平臺上執行Unix命令的能力, 包括Windows。
我在React彈弓項目中同時使用了方法#1和#2。
痛點
誠然,npm腳本也有一些缺點: JSON規範並不支持註釋, 因此你沒法在package.json文件中添加註釋。 這裏有幾個方法能夠圍繞這個限制開展工做:
1. 簡短、命名良好、目的單一的腳本
2. 單獨對腳本提供文檔(例如在一個README.md文件中)
3. 調用單獨的js文件
我傾向於選項#1. 若是你將每一個腳本都分解成只有單一職責, 將不多再須要註釋。腳本的名字能夠徹底描述其意圖,就好像全部簡短且命名良好的函數同樣。就像我在《簡潔代碼:編寫人能看懂的代碼》中的討論同樣,短小且單一職責的函數不多須要註釋。當我以爲註釋是必要的時,我使用選項#3並將腳本移到單獨的文件中。這讓我在須要時可使用javascript的全部能力。
Package.json也不支持變量。這聽起來像是一個大問題,但因爲如下兩個緣由,它再也不是問題。 首先,最一般的須要變量的狀況是要解決環境問題,但這你能夠在命令行中設置。其次,若是你由於其餘緣由須要用到變量,你徹底能夠調用一個單獨的js文件。 在React-starter-kit項目中你能夠找到該作法的一個優雅的例子。
最後,在建立很是長且很複雜命令行參數時,這依然是有風險的,由於這些命令很是難理解。 代碼審查和勤於重構是一個很好的方法,能夠保證npm腳本都被分解成簡短、命名良好且目的單一的函數,這能讓全部人都能理解這些腳本。 若是腳本複雜到須要註釋,你應該能夠很容易的將單個腳本重構成多個命名良好的腳本,或直接將其提取到一個單獨的文件中。
增長抽象概念必須理由充分
Gulp和Grunt都是將我使用的工具進行再次抽象。 抽象概念是有用的,可是抽象概念須要成本。 它們可能形成內存泄漏。 它們讓咱們依賴於插件維護者和他們的文檔。並且他們經過增長依賴項的數量來增長了複雜度。 我已經決定像Gulp和Grunt這樣的任務執行器的抽象概念我不再須要了。
想要更多細節? 在Pluralsight平臺(一家美國軟件開發在線教育平臺)上我解決了用npm scripts爲「用ES6編寫React和Redux」這個目標建立進程的問題。
評論? 能夠在文章底部、Reddit或Hacker News上進行評論。
最後, 我離第一個建議這麼作的人已經很遙遠。 下面是一些很是棒的連接:
· 用npm run完成任務自動化 -- James Holliday
· 使用npm腳本實現進階前端自動化 -- Kate Hudson
· 如何將npm用成一個構建工具 -- Kieth Cirkel
· npm做爲構建工具介紹 -- Marcus Hammarberg
· Gulp很是棒,可是咱們真的須要它嗎? -- Gonto
· NPM腳本之於構建工具 -- Andrew Burgess
…………
Cory House是《用React和Flux建立應用》、《ES6下的React和Redux》、《簡潔代碼:編寫人能看懂的代碼》和其餘多個Pluralsight上的課程的做者。 他是VinSolutions 的軟件架構師,而且經過像前端開發和簡潔代碼這些軟件實踐來訓練國際化的軟件工程師。Cory是一位微軟的MVP、Telerik開發專家同時仍是outlierdeveloper.com網站的創始人。
---------------------
謝謝觀看。 翻譯的不對歡迎指正。
2016.08.23