Why npm Scripts?

原文發表於 2016.2.12,原文地址: https://css-tricks.com/why-npm-scripts/

以下是訪客Damon Bauer發佈的一篇文章。近來直接使用node package提供的命令行界面的情緒持續高漲,反之,人們對通過運行任務從而屏蔽抽象功能的熱情逐漸降溫。在一定程度是,你無論如何都要使用npm,而同時npm提供了腳本功能,爲什麼不用呢?但是我們使用npm的腳本功能的原因有很多。Damon會幫我們理解這樣做的原因,並確切的告訴我們如何通過這種方式來完成前端構建過程中的大部分重要任務。

我已經在項目中使用npm scripts 有大概半年的時間。在這之前,我使用Gulp,再之前是 Grunt。 Gulp和Grunt很好用,他們高效的自動完成了很多我之前需要手動完成的工作,幫助我更快的工作。但是我卻開始覺得我在這些工具上花費的精力要比花在我自己代碼上的精力多得多。

Grunt, Gulp, Broccoli, Brunch和類似的工具都需要將自己的任務配置的適合他們的範型和結構,這些工具每一個都需要學習他們不同的語法、奇怪的用法和特有的方法。這增加了編碼複雜度、構建複雜度,使得你關注修復工具問題更甚於寫代碼。

由於這些構建工具依賴於包裝了核心命令行工具的插件,並基於這個核心工具做了進一步的抽象,這使得出錯的情況變得更加複雜。

以下三個問題我已經遇到過多次:

  • 如果當前的命令行工具中沒有你想用的插件,那麼你將束手無策(除非你自己寫一個)。
  • 你要用的插件包裝了舊版的工具,所以使用的插件和當前核心工具的特性、文檔不能夠完全匹配一致。
  • 錯誤提示不夠友好。如果插件掛了的話,很可能沒法一路追蹤這個錯誤直到核心工具,這時我很沮喪的發現自己並不清楚如何debug這個錯誤。

但是,請記住...

如果你對當前的構建系統很滿意,並且它能夠很好的完成你的需求的話,就請繼續使用吧!不要因爲npm scripts越來越流行就盲目的使用它,應該把精力集中在寫代碼而不是學習更多的工具。如果你開始覺得自己正在和使用的工具戰鬥,那麼這個時候我建議你考慮使用一下npm scripts。

如果你現在做好決定想要調研或使用npm scripts,那麼請繼續閱讀本文!本文將會提供大量的案例任務,同時基於這些任務我也創建了npm-build-boilerplate 以方便你學習。那麼下面讓我們開始吧!

寫npm scripts

我們會花費大量的時間在`package.json`文件上。這個文件描述了我們需要的所有的依賴和腳本。以下是我的boilerplate項目中的一部分內容:

複製代碼
 1 {
 2   "name": "npm-build-boilerplate",
 3   "version": "1.0.0",
 4   "scripts": {
 5     ...
 6   },
 7   "devDependencies": {
 8     ...
 9   }
10 }
複製代碼

 

接下來我們將逐步創建`package.json`文件。我們的腳本會寫入scripts對象中,所有我們要使用的工具都會被安裝並且寫入devDependencies對象中。在開始之前,以下是本文中的項目結構:

id="iframe_0.1335493025879826" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22https://cdn.css-tricks.com/wp-content/uploads/2016/02/directory.png?_=5204599%22%20style=%22border:none;max-width:1578px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.1335493025879826',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="border-width: initial; border-style: none; width: 500px; height: 719px;">

編譯scss爲css

我是SCSS的重度用戶,平時工作都依賴SCSS。爲了將SCSS編譯成CSS,我使用了node-sass。首先,我們需要安裝node-sass,在命令行下運行以下代碼:

npm install --save-dev node-sass

這個命令會在當前目錄下安裝node-sass,並添加到`package.json`的devDependencies對象中。當其他人使用你的項目時會非常方便,因爲他們已經有了運行項目所需的所有內容。只要安裝過一次,使用時在命令行運行以下代碼即可:

node-sass --output-style compressed -o dist/css src/scss
讓我們看一下這個命令做了什麼:從後向前看,查找`src/scss`目錄的SCSS文件,輸出(-o 標識)編譯的CSS到`dist/css`目錄,壓縮輸出文件(使用 --output-style 標識,設置選項值爲"compressed")。

現在我們知道了在命令行中如何工作,那麼讓我們把它放到npm scirpt中。在`package.json`的scripts對象中添加如下內容:

"scripts": {
  "scss": "node-sass --output-style compressed -o dist/css src/scss"
}

現在回到命令行並運行:

npm run scss
可以看到這樣運行的輸出結果和直接在命令行使用node-sass命令得到的結果一致。

本文剩餘部分創建的任何一個npm script,都可以像上例一樣使用命令行運行。

只要把你想要運行的任務名從scss替換成你想要的名字即可。

As you will see, many of the command line tools we'll use have numerous options you can use to configure it exactly you see fit. For instance, here's the list of (node-sass options). Here's a different setup show how to pass multiple options:

你將看到,我們使用的很多命令行工具都有很多的配置項,你可以使用配置項來精確完成你想要做的工作。比如,這是node-sass的選項列表,以下展示了傳多個配置項的配置方法:

"scripts": {
  "scss": "node-sass --output-style nested --indent-type tab --indent-width 4 -o dist/css src/scss"
}

使用PostCSS自動給CSS加前綴

我們已經能夠把SCSS編譯成CSS,現在我們希望通過Autoprefixer和PostCSS自動給CSS代碼添加廠商前綴,我們可以通過空格分隔的方式從而同時安裝多個模塊:

npm install --save-dev postcss-cli autoprefixer
因爲PostCSS默認不做任何事情,所以我們安裝了兩個模塊。PostCSS依賴其他的插件來處理你提供的CSS,比如Autoprefixer。

安裝並保存必要工具到devDependencies後,在你的scripts對象中添加一個新任務:

"scripts": {
  ...
  "autoprefixer": "postcss -u autoprefixer -r dist/css/*"
}

這個任務的意思是:嗨postcss,使用(-u標識符)Autoprefixer替換(-r標識符)`dist/css`目錄下的所有`.css`文件,給他們添加廠商前綴代碼。就是這樣簡單!想要修改默認瀏覽器前綴?只要給腳本添加如下代碼即可:

"autoprefixer": "postcss -u autoprefixer --autoprefixer.browsers '> 5%, ie 9' -r dist/css/*"
再次申明,配置你自己的構建代碼有很多選項可以使用:postcss-cliautoprefixer

JavaScript 代碼檢查

對於寫代碼來說,保持標準格式和樣式是非常重要的,它能夠確保錯誤最小化並提高開發效率。"代碼檢查"幫助我們自動化的完成了這個工作,所以我們通過使用 eslint 來進行代碼檢查。

再次如上文所述,安裝eslint的包,這次讓我們使用快捷方式:

npm i -D eslint

這和如下代碼是一樣的效果:

npm install --save-dev eslint
安裝完成後,我們給eslint配置一些運行代碼的基本規則。使用如下代碼開始一個嚮導:、
eslint --init // 譯者注:這裏直接使用會拋錯eslint找不到,因爲這種使用方法必須安裝在全局,即通過 npm install i -g eslint方式安裝
我建議選擇"回答代碼風格問題"並回答提問的相關問題。這個過程中eslint會在你的項目根目錄下生成一個新文件,並檢測你的相關代碼。

現在,讓我們把代碼風格檢測任務添加到`package.json`的scripts對象中:

"scripts": {
  ...
  "lint": "eslint src/js"
}

我們的任務僅有13字符!它會查找`src/js`目錄下的所有JavaScript文件,並根據剛纔生成的規則進行代碼檢測。當然,如果感興趣的話你可以詳細配置各種規則:get crazy with the options

混淆壓縮JavaScript文件

讓我們繼續,下面我們需要使用uglify-js混淆壓縮JavaScript文件,首先需要安裝uglify-js:

npm i -D uglify-js
然後我們可以在`package.json`裏創建壓縮混合任務:
"scripts": {
  ...
  "uglify": "mkdir -p dist/js && uglifyjs src/js/*.js -m -o dist/js/app.js"
}

npm scripts的任務的本質是:可以重複執行的、命令行任務的快捷方式(別名),這也是npm scripts的優點之一。這就意味着你可以直接在腳本里使用標準命令行代碼!這個任務使用了兩個標準命令行特性:mkdir 和 &&。

這個任務的第一部分“ mkdir -p dist/js ”:如果不存在目錄(-p標識)就創建一個目錄結構(mkdir),創建成功後執行uglifyjs命令。&&幫助你連接多個命令,如果前一個命令成功執行的話,就分別順序執行剩餘的命令。

這個任務的第二部分告訴uglifyjs針對`src/js/`目錄下的所有JS文件(`*.js`),使用"mangle"命令(-m 標識),輸出結果到`dist/js/app.js`文件中。這裏是uglifyjs工具的全部配置選項 list of options

讓我們來更新一下uglify任務,創建一個`dist/js/app.js`的壓縮版本,鏈接另外一個uglifyjs的命令並傳參給"compress"(-c標識)。

"scripts": {
  ...
  "uglify": "mkdir -p dist/js && uglifyjs src/js/*.js -m -o dist/js/app.js && uglifyjs src/js/*.js -m -c -o dist/js/app.min.js"
}

壓縮圖片

下面我們將進行圖片壓縮的工作。根據httparchive.org的數據統計,網絡上前1000名的網站平均頁面大小爲1.9M,其中圖片佔了1.1M(with images accounting for 1.1mb of that total)。所以提高網頁加載速度的其中一個好辦法就是減小圖片大小。

安裝 imagemin-cli:

npm i -D imagemin-cli
Imagemin非常棒,它可以壓縮大多數圖片類型,包括GIF、JPG、PNG和SVG。 使用如下代碼可以將一整個文件夾的圖片全部壓縮:
"scripts": {
  ...
  "imagemin": "imagemin src/images dist/images -p",
}

這個任務告訴imagemin找到並壓縮`src/images`中的所有圖片並輸出到`dist/images`中。-p標誌在允許的情況下將圖片處理成漸進圖片。更多配置可查看文檔 all available options

SVG精靈(Sprites)

關於SVG的討論近年來逐漸火熱,SVG有衆多優點:在所有的設備上保持鬆散結構、可通過CSS編輯、對讀屏軟件友好。然而,SVG編輯軟件經常會產生大量的冗餘代碼。幸運的是,svgo可以幫助你自動刪除這些冗餘信息(我們馬上就會安裝svgo)。

接下來我們來安裝svg-sprite-generator,用於自動處理並整合多個SVG文件爲一個SVG文件(更多處理方案:more on that technique here)。

npm i -D svgo svg-sprite-generator

你現在應該已經熟悉了這個過程——添加一個任務在你的`package.json`scripts對象中:

"scripts": {
  ...
  "icons": "svgo -f src/images/icons && mkdir -p dist/images && svg-sprite-generate -d src/images/icons -o dist/images/icons.svg"
}

注意icons任務通過兩個&&引導符做了三件事情: 1.使用svgo傳參一個SVG目錄(-f標識),這個操作壓縮了目錄內的全部SVG文件;2.如果不存在'dist/images'目錄則創建該目錄(使用mkdir -p命令);3.使用svg-sprite-generator,傳參一個SVG目錄(-d標識)以及輸出處理後的SVG文件的目錄路徑名(-o標識)。

通過BrowserSync提供服務、自動監測並注入變更

最後一個插件是BrowserSync,它可以做如下事情:啓動一個本地服務,向連接的瀏覽器自動注入更新的文件,並同步瀏覽器的點擊和滾動。安裝並添加任務的代碼如下:

npm i -D browser-sync
"scripts": {
  ...
  "serve": "browser-sync start --server --files 'dist/css/*.css, dist/js/*.js'"
}

BrowserSync任務默認使用當前根目錄下的路徑啓動一個服務器(--server標識),--files標識告訴BrowserSync去監測`dist`目錄的CSS或JS文件,一旦有任何變化,則自動向頁面注入變化的文件。

你可以同時打開多個瀏覽器(甚至在不同的設備上),他們都會實時更新文件變化的!

分組任務

使用以上任務我們可以做到如下功能:

  • 編譯SCSS到CSS並自動添加廠商前綴
  • 對Javascript進行代碼檢查及混淆壓縮
  • 壓縮圖片
  • 整合整個文件夾內的SVG文件爲一個SVG文件
  • 啓動一個本地服務並向連接至該服務的瀏覽器自動注入更新。

這還不是全部內容!

合併CSS任務

Let's add a task that combines the two CSS related tasks (preprocessing Sass and running Autoprefixer), so we don't have to run each one separately:

我們會添加一個新的任務,用於合併兩個CSS相關的任務(處理SASS和執行加前綴的Autoprefixer),有了這個任務我們就不用分別執行兩個相關任務了:

"scripts": {
  ...
  "build:css": "npm run scss && npm run autoprefixer"
}

當你運行npm run build:css時,這個任務會告訴命令行去執行npm run scss,當這個任務成功完成後,會接着(&&)執行 npm run autoprefixer。

就像這個build:css任務一樣,我們可以把JavaScript任務也鏈接到一起以方便執行:

合併JavaScript任務

"scripts": {
  ...
  "build:js": "npm run lint && npm run uglify"
}

現在,我們可以通過npm run build:js一步調用,來進行代碼檢測、混淆壓縮JavaScript代碼了!

合併剩餘任務

對於圖片任務、其他剩餘構建任務,我們可以用相同的方法把他們變成一個任務:

"scripts": {
  ...
  "build:images": "npm run imagemin && npm run icons",
  "build:all": "npm run build:css && npm run build:js && npm run build:images",
}

變更監控

至此,我們的任務不斷的需要對文件做一些變更,我們不斷的需要切回命令行去運行相應的任務。針對這個問題,我們可以添加一個任務來監聽文件變更,讓文件在變更的時候自動執行這些任務。這裏我推薦使用onchange插件,安裝方法如下

npm i -D onchange
讓我們來給CSS和JavaScript設置監控任務:
"scripts": {
  ...
  "watch:css": "onchange 'src/scss/*.scss' -- npm run build:css",
  "watch:js": "onchange 'src/js/*.js' -- npm run build:js",
}

這些任務可以分解如下:onchange需要你傳參想要監控的文件路徑(字符串),這裏我們傳的是SCSS和JS源文件,我們想要運行的命令跟在--之後,這個命令當路徑內的文件發生增刪改的時候就會被立即執行。

讓我們再添加一個監控命令來完成我們的npm scripts構建過程。

再添加一個包,parallelshell

npm i -D parallelshell
再次給scriopts對象添加一個新任務:
"scripts": {
  ...
  "watch:all": "parallelshell 'npm run serve' 'npm run watch:css' 'npm run watch:js'"
}

parallelshell支持多個參數字符串,它會給npm run傳多個任務用於執行。

爲什麼時候parallelshell去合併多個任務,而不是像之前的任務一樣使用&&呢?最開始我也嘗試這麼做了,但是問題是:&&鏈接多個命令到一塊,需要等待每一個命令成功完成後纔會執行下一個任務。然而當我們運行watch命令時,這些命令一直都不會結束!這樣我們就會卡在一個無限循環裏。

因此,使用parallelshell使得我們可以同時執行多個watch命令。(譯者注:後來在評論裏有人推薦使用npm-run-all插件來代替parallelshell,它支持這種用法可以一次性檢測全部watch任務更加方便:"watch": "npm-run-all --parallel serve watch:*")

這個任務使用了BrowserSync的npm run serve任務啓動了一個服務,然後對CSS和JavaScript文件執行了監控命令,一旦CSS或JavaScript文件有變更,監控任務就會執行相應的構建(build)任務。由於BrowserSync被設置成監控`dist`目錄下的變更,所以它會自動的向相關聯的URL內注入新的文件,真是太棒了!

其他實用任務

npm有大量可以實用的插件(ots of baked in tasks ),讓我們再添加一個新的任務來看看這些插件對構建腳本的影響:

"scripts": {
  ...
  "postinstall": "npm run watch:all"
}

當你在命令行中執行npm install的時候postinstall會立即執行,當團隊合作時這個功能非常有用。當別人複製了你的項目並運行了npm install的時候,我們的watch:all任務就會馬上執行,別人馬上就會準備好一切開發環境:啓動一個服務、打開一個瀏覽器窗口、監控文件變更。

打包

太棒了!我希望你能夠通過本文學習了一些使用npm scripts構建過程和常用命令行的方法。

萬一你忘記了什麼知識點,我用以上所有提到的任務創建了一個npm-build-boilerplate 項目以方便你學習。如果有任何問題或者評論,請推特我( tweet at me)或者在下方留言,我會盡我所能的幫助您!

 

---- 翻譯完 ----

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章