经上篇『手撕Vue-CLI』添加帮助和版本号的介绍之后,已经可以在控制台中输入 nue --help
来查看帮助信息了,但是在帮助信息中只有 --version
,--help
这两个指令,而 vue-cli 中还有很多指令,例如 create
,serve
,build
等等,所以本章将继续添加自定义指令,例如 create
指令。
在 vue-cli 中,create
指令是用来创建一个新的项目的,我实现的 nue --help
的帮助信息中只有 --version
,--help
这两个指令,所以当用户使用我的 nue-cli 时,并不知道有 create
这个指令,所以接下来要完成的就是添加 create
指令到 nue-cli
--help 的帮助信息中。
是否大家还记得在上一篇『手撕Vue-CLI』添加帮助和版本号中,我引入了 commander
这个库,这个库是用来处理命令行参数的,所以我们可以使用这个库来添加 create
指令到 nue-cli
--help 的帮助信息中。
首先我们需要在 /bin/index.js
中引入 commander
这个库,这一步上一篇已经完成了,所以这里就不再赘述。
然后需要在 /bin/index.js
中添加 create
指令,这里我们可以使用 commander
的 command
方法来添加指令,如下:
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
指令了:
其实就几点,介绍了一下 commander
的 command
,description
,alias
,action
方法,这几个方法是用来添加指令的,设置指令的描述信息,别名,回调函数的,这样就可以添加自定义指令了。
有个注意点大家需要注意一下,就是 program.version(version).parse(process.argv);
这行代码要放在所有指令的后面,不然指令就不会生效了。
指令添加完成了,但是呢有一个问题,因为我本人是比较熟悉 vue-cli 所以知道有 create 并且知道怎么用,那么如果是一个新手呢?如果他知道了有 create 但是不知道怎么用呢?所以还需要添加 create
指令的使用示例。
这个我相信对于新手又或者说有经验的人来说使用示例是啥就不用多说了,就是告诉用户怎么用这个指令。
那么如何添加 create
指令的使用示例呢?紧接着上面的代码,其实在 commander 中也有对应的解决方案,就是通过 commander.on
进行监听,监听 --help
事件,然后在监听事件中添加 create
指令的使用示例,如下:
+ program.on('--help', () => {
+ console.log('');
+ console.log('Examples:');
+ console.log(' nue create <app-name> ');
+ });
为啥我还要起一个标题来说这个呢?我现在只有一个 create 自定义指令,那么在后面还会有很多自定义指令,那么每次都要写一遍 command
,description
,alias
,action
,on
这些方法,那么这样就会显得很冗余,所以可以封装一个公共解决方案,这样就可以减少代码量,提高代码的可维护性。
首先定义一个 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: '',
+ },
+ };
我这里定义了 create
,add
,*
三个指令,*
是用来处理用户输入的指令不存在的情况,这里只是定义了三个指令,后续还可以继续添加。
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
就可以看到所自定义的指令了: