構建現代化的命令行工具

文章源於 lambdas.devgit


每當咱們想要建立一個基於 NodeJS 的命令行工具時,就會衍生出一堆問題須要解決,好比如何準備開發環境,如何打包轉譯代碼,如何使代碼在轉譯後保持可調用的狀態同時儘量的壓縮體積, 以及怎樣設計項目分配 CommandOption 等等,這會浪費巨大的時間,並且並不是必定有成果。這時你能夠注意到社區幾乎全部的命令行工具都是自成一派, 並無嚴謹的框架或約定約束,也無所謂的最佳實踐,這使想要特別是第一次想要開發命令行工具的開發者望而卻步,或是幾番努力最後卻不盡如人意。github


舉個例子來講,騰訊的 omi 是一個有衆多使用者的框架,但其命令行工具 omi / omi-cli 卻讓人貽笑大方。僅一些簡單的下載和建立模板的任務,造出長篇大論的文件不說,下載 時依賴數千,包的體積巨大,總體項目毫無設計幾乎是爲所欲爲、天馬行空,這就是開發者自己並不擅長此道,只學會了 糊屎。(何謂 糊屎,參閱 JS 優雅指南 2npm

func 的出現就是爲了解決這些問題。func 自己的實現參閱了社區內諸多基於 NodeJS 的命令行工具的優秀實現,與流行的框架設計思路相結合,以優雅的設計、小體積、高性能 等爲目標, 同時關注開發者體驗,大幅度的提高了命令行工具項目的可擴展性與可讀性,幾乎是現在 NodeJS 社區中開發命令行工具的最優解。咱們能夠嘗試使用 func 構建一個命令行工具。c#


構建項目

在之前流行的一些命令行參數解析的庫中,咱們在構建項目前須要準備大量的腳本與配置,甚至還要解決文件權限、bin、代碼轉譯等等問題,但使用 func,咱們能夠僅經過一行命令 初始化項目:bash

npm init func
複製代碼

項目初始化後進入文件夾,隨機使用 npm installyarn 安裝依賴,如今就能夠正式開發了。 能夠注意到,func 的項目模板中爲咱們準備了 startbuild 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 會在運行時爲你注入全部的命令參數, 一樣的也支持 OptionArgsProviderRegisterProvider 等等,你能夠在 官方的文檔 閱讀它們的具體類型。工具


打包與發佈

運行 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 庫中,咱們一般會有異步和同步函數兩種選擇,如 readFilereadFileSync,雖然同步函數能夠爲你節約一些開發時間, 但也會阻塞你的代碼,不少狀況下會有難以理解的問題。好比當你設置定時器顯示一個 Loading 圖標的同時操做了同步 API,那麼你的 Loading 圖標就會由於阻塞 而沒法運動 (由於沒法 render 到終端),或是你同時操做多個文件,同步的 API 會使你花費巨大的時間。

  • 不要發佈無用信息。

    命令行工具不少時候的角色是充當複雜的腳本,性能和體驗是相當重要的,發佈無用的信息在你的 package 中會使下載時間更長。(使用 files 來約束髮布的文件)

  • 不要修改臨時文件夾與配置區之外的信息。

    對於命令行工具來講,運行時的權限是巨大的,但不要所以弄髒用戶的系統。你可使用 require('os').tmpdir() 獲取用戶操做系統的臨時文件夾目錄,不管什麼時候, 你都擁有這裏的寫權限。

相關文章
相關標籤/搜索