文章源於 lambdas.devgit
每當咱們想要建立一個基於 NodeJS 的命令行工具時,就會衍生出一堆問題須要解決,好比如何準備開發環境,如何打包轉譯代碼,如何使代碼在轉譯後保持可調用的狀態同時儘量的壓縮體積, 以及怎樣設計項目分配 Command
與 Option
等等,這會浪費巨大的時間,並且並不是必定有成果。這時你能夠注意到社區幾乎全部的命令行工具都是自成一派, 並無嚴謹的框架或約定約束,也無所謂的最佳實踐,這使想要特別是第一次想要開發命令行工具的開發者望而卻步,或是幾番努力最後卻不盡如人意。github
舉個例子來講,騰訊的 omi
是一個有衆多使用者的框架,但其命令行工具 omi
/ omi-cli
卻讓人貽笑大方。僅一些簡單的下載和建立模板的任務,造出長篇大論的文件不說,下載 時依賴數千,包的體積巨大,總體項目毫無設計幾乎是爲所欲爲、天馬行空,這就是開發者自己並不擅長此道,只學會了 糊屎。(何謂 糊屎,參閱 JS 優雅指南 2)npm
func 的出現就是爲了解決這些問題。func
自己的實現參閱了社區內諸多基於 NodeJS 的命令行工具的優秀實現,與流行的框架設計思路相結合,以優雅的設計、小體積、高性能 等爲目標, 同時關注開發者體驗,大幅度的提高了命令行工具項目的可擴展性與可讀性,幾乎是現在 NodeJS 社區中開發命令行工具的最優解。咱們能夠嘗試使用 func
構建一個命令行工具。c#
在之前流行的一些命令行參數解析的庫中,咱們在構建項目前須要準備大量的腳本與配置,甚至還要解決文件權限、bin
、代碼轉譯等等問題,但使用 func
,咱們能夠僅經過一行命令 初始化項目:bash
npm init func
複製代碼
項目初始化後進入文件夾,隨機使用 npm install
或 yarn
安裝依賴,如今就能夠正式開發了。 能夠注意到,func
的項目模板中爲咱們準備了 start
與 build
2 個腳本,它們都是由 func-service
驅動的,幫助你一鍵切換開發與生產模式,咱們所要作的就是專一於 命令行邏輯自己,實現邏輯就夠了。框架
咱們能夠隨意的建立一個類,當它被加上 Command
註解時這就是一個命令,而被加上 Option
註解時就會轉變爲一個選項:異步
import { Command } from 'func'
@Command({ name: 'test' })
export class Test {
}
複製代碼
在命令行中運行 <YOUR NAME> test
時,Test
類將會被調用。選項也是同理。你能夠在 constructor
中開始本身的代碼,這和你在任何地方寫 NodeJS 沒有什麼 不一樣,當你以爲差很少的時候,運行 npm build
就能夠將它打包,一切就是這麼簡單。ide
有時候,當咱們須要使用到命令行攜帶的參數時,好比處理 <NAME> something params -option
這樣複雜的輸入,你能夠直接在類中注入命令行參數,對,就是像 IoC 那樣:函數
@Command({ name: 'test' })
export class Test {
constructor( private args: CommandArgsProvider, ) {
}
}
複製代碼
CommandArgsProvider
其實是一個 class
類型,當你標記一個參數爲此類型時,func
會在運行時爲你注入全部的命令參數, 一樣的也支持 OptionArgsProvider
、RegisterProvider
等等,你能夠在 官方的文檔 閱讀它們的具體類型。工具
運行 npm build
能夠獲得一個打包後的文件,這是由 ncc
編譯後的文件,一般它只有一個 (若是攜帶 extra 可能會有多個,但它們會自動連接), 同時爲你連接好了 bin
,你要作的惟一一件事就是將包發佈出去。爲何 func
使用這種方式發佈呢?
咱們知道當你在安裝一個包或是使用 npx
執行包時 (這在使用命令行工具的人羣中很常見),NPM 所花費的時間大約在 3 個部分,即對比包的依賴,下載包,執行。 首先咱們知道 func
的項目足夠的小,可以大量介紹下載時間,同時也有足夠好的性能,如今要解決的就是在大量依賴時的對比分析問題,將文件打包成單文件不依賴 外部環境時會極大的減小所需時間,若是你再將全部的依賴移入 devDependencies
中,幾乎可以在一瞬間完成 分析 - 下載 - 運行 這三個步驟。這樣的體驗是不可思議的。 是的,這裏推薦你把全部的依賴當作開發依賴處理,這彷佛違背了 NPM Package 的開發哲學,但在使用 func
構建命令行應用時這樣作卻大有裨益。
在運行 func
build 完成的包時,咱們注意到幾乎無需任何依賴,這是由於在單個文件中已經 bundle
了全部的所需資源,也就意味着用戶在運行 .js
文件時, 堆棧中真的就只有 .js
文件內的內容,不會引用其餘,不會加載任何可有可無的東西。此時咱們也就無需用戶關心 dependencies
,甚至能夠移除它們,這樣一來, 下載或即時運行時就直接跳過了 對比依賴版本 這一步,這其中省略了無數的請求也就會會極大的增長速度,npm init func
可以在 1 秒左右馬上開始安裝也是這樣的道理。
如今你已經知道了怎樣快速的構建一個合格且優雅的命令行工具,那怎樣作的更好呢?一般來講你須要遵循這幾點:
不由於小功能引入巨大的包,不引入依賴爆炸的包。
舉例來講,download-git-repo
是一個很不錯的包,它可以爲你節約不少時間,但請注意它依賴了 download
,若是你僅爲了下載單個文件或只有不多的下載需求時, 這就顯得有些大材小用,download
會爲你增長約 450 kb 的重量,卻只作了一件你 5 分鐘能夠搞定的事情。一樣你的用戶也會爲此付出巨大的時間代價。
不要顯示錯誤堆棧信息。
在多數狀況下咱們都須要儘量的顯示堆棧或是引用的錯誤信息便於 debug,可是在命令行工具中這樣作只會使你的用戶很是困惑。這主要歸結於命令行中不能很好高亮 的顯示代碼塊,大量的代碼信息會使用戶不知所措。建議你始終構建一個錯誤處理模塊來解決問題,同時爲用戶提供良好的反饋,最後能夠提供相似於 --debug
的 選項讓開發者調試。
不要太依賴同步操做。
在 NodeJS 與其社區流行的 I/O 庫中,咱們一般會有異步和同步函數兩種選擇,如 readFile
與 readFileSync
,雖然同步函數能夠爲你節約一些開發時間, 但也會阻塞你的代碼,不少狀況下會有難以理解的問題。好比當你設置定時器顯示一個 Loading 圖標的同時操做了同步 API,那麼你的 Loading 圖標就會由於阻塞 而沒法運動 (由於沒法 render 到終端),或是你同時操做多個文件,同步的 API 會使你花費巨大的時間。
不要發佈無用信息。
命令行工具不少時候的角色是充當複雜的腳本,性能和體驗是相當重要的,發佈無用的信息在你的 package 中會使下載時間更長。(使用 files
來約束髮布的文件)
不要修改臨時文件夾與配置區之外的信息。
對於命令行工具來講,運行時的權限是巨大的,但不要所以弄髒用戶的系統。你可使用 require('os').tmpdir()
獲取用戶操做系統的臨時文件夾目錄,不管什麼時候, 你都擁有這裏的寫權限。