『手撕Vue-CLI』新增自定義指令

BNTang發表於2024-05-19

前言

經上篇『手撕Vue-CLI』新增幫助和版本號的介紹之後,已經可以在控制檯中輸入 nue --help 來檢視幫助資訊了,但是在幫助資訊中只有 --version--help 這兩個指令,而 vue-cli 中還有很多指令,例如 createservebuild 等等,所以本章將繼續新增自定義指令,例如 create 指令。

新增 create 指令

在 vue-cli 中,create 指令是用來建立一個新的專案的,我實現的 nue --help 的幫助資訊中只有 --version--help 這兩個指令,所以當使用者使用我的 nue-cli 時,並不知道有 create 這個指令,所以接下來要完成的就是新增 create 指令到 nue-cli --help 的幫助資訊中。

新增 create 指令到 --help 的幫助資訊中

是否大家還記得在上一篇『手撕Vue-CLI』新增幫助和版本號中,我引入了 commander 這個庫,這個庫是用來處理命令列引數的,所以我們可以使用這個庫來新增 create 指令到 nue-cli --help 的幫助資訊中。

首先我們需要在 /bin/index.js 中引入 commander 這個庫,這一步上一篇已經完成了,所以這裡就不再贅述。

然後需要在 /bin/index.js 中新增 create 指令,這裡我們可以使用 commandercommand 方法來新增指令,如下:

command 方法接收一個引數,第一個引數是指令的名稱,呼叫方式是透過 commander 例項呼叫 command 方法,如下:

+ program
+   .command('create');

這樣我們就新增了 create 指令:

這裡只是單單的新增了 create 指令,但是並沒有新增 create 指令的描述資訊,告訴一下使用者這個指令是幹嘛幹嘛用的之類的話術,所以我們需要新增 create 指令的描述資訊,如下:

那麼如何新增 create 指令的描述資訊呢?緊接著上面的程式碼,在 command 方法後面新增 description 方法鏈式呼叫, description 方法的作用就是新增指令的描述資訊,接收一個引數,就是指令的描述資訊,如下:

 program
   .command('create')
+  .description('create a new project powered by nue-cli-service');

好了指令的描述設定好了,還可以設定下 alias 別名,就是可以透過簡寫的方式進行使用指令,繼續鏈式呼叫 alias 方法,alias 方法的作用就是設定指令的別名,接收一個引數,就是指令的別名,如下:

 program
   .command('create')
+  .alias('c')
   .description('create a new project powered by nue-cli-service');

還可以設定 action 方法,繼續鏈式呼叫 action 方法,action 方法的作用就是設定指令的回撥函式,當使用者輸入了這個指令的時候,就會執行這個回撥函式,如下:

 program
   .command('create')
   .alias('c')
   .description('create a new project powered by nue-cli-service')
+  .action(() => {
+    console.log('建立一個 Nue 專案');
+  });

這樣我們就新增了 create 指令,並且新增了 create 指令的描述資訊,別名,回撥函式,現在如果使用者使用 nue --help 就可以看到 create 指令了:

總結

其實就幾點,介紹了一下 commandercommanddescriptionaliasaction 方法,這幾個方法是用來新增指令的,設定指令的描述資訊,別名,回撥函式的,這樣就可以新增自定義指令了。

有個注意點大家需要注意一下,就是 program.version(version).parse(process.argv); 這行程式碼要放在所有指令的後面,不然指令就不會生效了。

指令新增完成了,但是呢有一個問題,因為我本人是比較熟悉 vue-cli 所以知道有 create 並且知道怎麼用,那麼如果是一個新手呢?如果他知道了有 create 但是不知道怎麼用呢?所以還需要新增 create 指令的使用示例。

新增 create 指令的使用示例

這個我相信對於新手又或者說有經驗的人來說使用示例是啥就不用多說了,就是告訴使用者怎麼用這個指令。

那麼如何新增 create 指令的使用示例呢?緊接著上面的程式碼,其實在 commander 中也有對應的解決方案,就是透過 commander.on 進行監聽,監聽 --help 事件,然後在監聽事件中新增 create 指令的使用示例,如下:

+ program.on('--help', () => {
+   console.log('');
+   console.log('Examples:');
+   console.log('  nue create <app-name>  ');
+ });

封裝公共解決方案

為啥我還要起一個標題來說這個呢?我現在只有一個 create 自定義指令,那麼在後面還會有很多自定義指令,那麼每次都要寫一遍 commanddescriptionaliasactionon 這些方法,那麼這樣就會顯得很冗餘,所以可以封裝一個公共解決方案,這樣就可以減少程式碼量,提高程式碼的可維護性。

首先定義一個 commandMap 物件,用來存放指令的資訊,可以將後續需要使用的指令都放裡面進行存放起來,如下:

+ const commandMap = {
+   create: {
+     alias: 'c',
+     description: 'create a new project powered by vue-cli-service',
+     example: 'nue-cli create <app-name>',
+   },
+   add: {
+     alias: 'a',
+     description: 'install a plugin and invoke its generator in an already created project',
+     example: 'nue-cli add [options] <plugin> [pluginOptions]',
+   },
+   '*': {
+     alias: '',
+     description: 'command not found',
+     example: '',
+   },
+ };

我這裡定義了 createadd* 三個指令,* 是用來處理使用者輸入的指令不存在的情況,這裡只是定義了三個指令,後續還可以繼續新增。

commandMap 物件的取值就是指令的名稱,然後值是一個物件,這個物件包含了指令的別名,描述資訊,使用示例,欄位,後續的改進就是遍歷 commandMap 物件,迴圈的新增指令,如下:

- program
-   .command('create')
-   .alias('c')
-   .description('create a new project powered by nue-cli-service')
-   .action(() => {
-     console.log('建立一個 Nue 專案');
-   });
+ Reflect.ownKeys(commandMap).forEach((key) => {
+   const value = commandMap[key];
+   program
+     .command(key)
+     .alias(value.alias)
+     .description(value.description)
+     .action(() => {
+       if (key === '*') {
+         console.log(value.description);
+       } else {
+         console.log(value.description);
+       }
+     });
+ });

透過 Reflect.ownKeys 方法遍歷 commandMap 物件,然後透過 program.command 方法新增指令,Reflect.ownKeys 這個是 ES6 提供的一個方法,用來獲取物件自身的屬性鍵,返回一個陣列,這個方法是用來替代 Object.keys 方法的,Object.keys 方法只能獲取物件自身的可列舉屬性鍵,而 Reflect.ownKeys 方法可以獲取物件自身的所有屬性鍵,包括不可列舉屬性鍵。

什麼意思呢?就是說 Reflect.ownKeys 方法可以獲取物件自身的所有屬性鍵,包括不可列舉屬性鍵,而 Object.keys 方法只能獲取物件自身的可列舉屬性鍵,所以 Reflect.ownKeys 方法更加強大。

不可列舉又是什麼意思呢?通俗易通的說就是 private 與 public 的區別,private 是不可列舉的,public 是可列舉的。

透過這麼一改造之後,之前透過 command 方法新增指令的程式碼寫法已經最佳化完畢了,還有一個就是新增指令所對應的使用示例,程式碼也需要進行最佳化,如下:

- program.on('--help', () => {
-   console.log('');
-   console.log('Examples:');
-   console.log('  nue create <app-name>  ');
- });
+ program.on('--help', () => {
+     console.log('Examples:');
+     Reflect.ownKeys(commandMap).forEach((key) => {
+         const value = commandMap[key];
+         console.log(`  ${value.example}`);
+     });
+ });

改寫方式就是透過 Reflect.ownKeys 方法遍歷 commandMap 物件,然後透過 console.log 方法輸出指令的使用示例。

最後在控制檯在輸入 nue --help 就可以看到所自定義的指令了:

相關文章