1. 程式人生 > 實用技巧 >NodeJS 構建現代化的命令列工具

NodeJS 構建現代化的命令列工具

每當我們想要建立一個基於 Nodejs的命令列工具時,就會衍生出一堆問題需要解決,比如如何準備開發環境,如何打包轉譯程式碼,如何使程式碼在轉譯後保持可呼叫的狀態同時儘可能的壓縮體積,以及怎樣設計專案分配Command與Option等等,這會浪費巨大的時間,而且並非一定有成果。這時你可以注意到社群幾乎所有的命令列工具都是自成一派,並沒有嚴謹的框架或約定約束,也無所謂的最佳實踐,這使想要特別是第一次想要開發命令列工具的開發者望而卻步,或是幾番努力最後卻不盡如人意。

舉個例子來說,騰訊的omi是一個有眾多使用者的框架,但其命令列工具omi/omi-cli卻讓人貽笑大方。僅一些簡單的下載和建立模板的任務,造出長篇大論的檔案不說,下載時依賴數千,包的體積巨大,整體專案毫無設計幾乎是隨心所欲、天馬行空,這就是開發者本身並不擅長此道,只學會了糊屎

。(何謂糊屎,參閱JS 優雅指南 2)

FUNC的出現就是為了解決這些問題。func本身的實現參閱了社群內諸多基於 NodeJS 的命令列工具的優秀實現,與流行的框架設計思路相結合,以優雅的設計、小體積、高效能 等為目標,同時關注開發者體驗,大幅度的提升了命令列工具專案的可擴充套件性與可讀性,幾乎是如今 NodeJS 社群中開發命令列工具的最優解。我們可以嘗試使用func構建一個命令列工具。

構建專案

在以前流行的一些命令列引數解析的庫中,我們在構建專案前需要準備大量的指令碼與配置,甚至還要解決檔案許可權、bin、程式碼轉譯等等問題,但使用func,我們可以僅通過一行命令
初始化專案:

npm init func

專案初始化後進入資料夾,隨機使用npminstall或yarn安裝依賴,現在就可以正式開發了。可以注意到,func的專案模板中為我們準備了start與build2 個指令碼,它們都是由func-service驅動的,幫助你一鍵切換開發與生產模式,我們所要做的就是專注於命令列邏輯本身,實現邏輯就夠了。

實現邏輯

我們可以隨意的建立一個類,當它被加上Command註解時這就是一個命令,而被加上Option註解時就會轉變為一個選項:

import { Command } from 'func'

@Command({ name: 'test' })
export class Test {
}

在命令列中執行<YOUR NAME> test時,Test類將會被呼叫。選項也是同理。你可以在constructor中開始自己的程式碼,這和你在任何地方寫 NodeJS 沒有什麼不同,當你覺得差不多的時候,執行npm build就可以將它打包,一切就是這麼簡單。

有時候,當我們需要使用到命令列攜帶的引數時,比如處理<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構建命令列應用時這樣做卻大有裨益。

在執行funcbuild 完成的包時,我們注意到幾乎無需任何依賴,這是因為在單個檔案中已經bundle了所有的所需資源,也就意味著使用者在執行.js檔案時,堆疊中真的就只有.js檔案內的內容,不會引用其他,不會載入任何無關緊要的東西。此時我們也就無需使用者關心dependencies,甚至可以移除它們,這樣一來,下載或即時執行時就直接跳過了對比依賴版本這一步,這其中省略了無數的請求也就會會極大的增加速度,npm init func能夠在 1 秒左右立刻開始安裝也是這樣的道理。

資源搜尋網站大全 https://www.renrenfan.com.cn 廣州VI設計公司https://www.houdianzi.com

優化與經驗

現在你已經知道了怎樣快速的構建一個合格且優雅的命令列工具,那怎樣做的更好呢?通常來說你需要遵循這幾點:

不因為小功能引入巨大的包,不引入依賴爆炸的包。

舉例來說,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()獲取使用者作業系統的臨時資料夾目錄,無論何時,你都擁有這裡的寫許可權。