原文发表在 Github: hoperyy blog
概览
commander.js 7.0.0版本的核心代码就一个文件index.js
,2200多行代码,代码的注释比较丰富,代码可读性也是不错的,感兴趣的同学可以通读一下。
commander.js包含以下类:
Option
Help
Command
CommandError
InvalidOptionArgumentError
如下图:
Option类
Options类的实例存储选项的各类信息:
- 选项是否必填(requred)
- 描述信息(description)
- 可变参数(variadic)
- 是否反向boolean,也就是
--no-xx
类型的选项 - 长名称/短名称,也就是
-c, --cheese
- 参数的值
- 其他
命令行实例执行program.option("-c, --cheese", "add cheese")
,会创建一个新的Option
实例,存储该选项的各类信息。
Option类内部通过各种正则和字符串的计算各类信息。
比如,Option类接受参数的长短名称有三种写法:
"-c, --cheese"
"-c|--cheese"
"-c --cheese"
其解析参数的时候以正则/[ |,]+/
对字符串做分隔计算: flags.split(/[ |,]+/)
。
Help类
Help类主要负责帮助信息的展示、配置等工作。
比如执行命令行node index.js -h
会打印出:
Usage: index [options]
Options:
-v, --version output the version number
-d, --debug output extra debugging
-c, --cheese <type> cheese type (default: "blue")
-b, --banana [type] banana type
-i, --integer <number> integer argument
-h, --help display help for command
Help类中相对值得说的是formatHelp
方法了。
formatHelp
接收当前命令行和help实例对象,返回格式化后的帮助信息。
在生成帮助信息的过程中,有几个小的编程技巧可以借鉴:
-
日志信息字符串,通过数组形式组织,最终拼接为字符串
该方法对比直接拼接字符串,代码可维护性更强。
-
利用
String.prototype.padEnd
方法实现字符串补全日常工作中用到该方法的场景可能不多,容易遗漏。
比如:
'hello'.padEnd(7, '~')
的结果是hello~~
。在
formatHelp
里用空格补全,用于字符串显示的格式化。
Command类
Command类是commander.js的核心类,提供了命令行的各类方法。
下图是Command类使用时的主要流程:
我们简要介绍下其中的一些点:
-
version(str, flags, description)
该方法注册了命令的版本信息,利用
createOption()
实现的一个快捷方法。 -
command(nameAndArgs, actionOptsOrExecDesc, execOpts)
该方法注册子命令,有两种模式:
-
绑定函数实现命令
program .command('start') .action(function() { console.log('actor'); });
执行
node index start
的时候,会执行action
注册的回调,打印actor
。 -
启动独立文件执行命令
program.command('start', 'start runner');
执行
node index start
的时候,会启动index-start.js
文件。
该方法内部通过是否含有描述信息判断是哪种模式。
-
-
重复注册命令时,会使用第一个注册的命令
比如:
program .command('start') .action(function() { console.log('start 1'); }); program .command('start') .action(function() { console.log('start 2'); });
在执行
node index start
的时候,只会打印start 1
,因为内部找到匹配的命令的代码是:this.commands.find(cmd => cmd._name === name || cmd._aliases.includes(name));
Array.prototype.find
方法会返回数组第一个匹配的元素。 -
EventEmitter在Command类中的使用
Node内置模块
EventEmitter
提供了事件机制,最常见的api是on/emit
。Command类中几处利用事件机制的地方举例:
- 注册选项参数时,会注册
option:${optionName}
事件(on(option:${optionName})
),在命令行执行时触发回调(emit(option:${optionName})
)。 - 执行命令时,如果没有匹配的命令,会通过
this.listenerCount('command:*')
获取command:xx
事件(*
为通配符)的监听者数量,决定是否触发该事件
- 注册选项参数时,会注册
Error类
下图是Commander.js内部定义的几个Error类的继承关系。
在内部实现上,分别定义了每个类自身的特殊字段。但值得注意的是,Error.captureStackTrace(this, this.constructor)
被频繁使用。
-
Error.captureStackTrace
使用Error.captureStackTrace(targetObject[, constructorOpt])
其作用是在
targetObject
中添加一个stack
属性。当访问targetObject.stack
时,将以字符串的形式返回Error.captureStackTrace
方法被调用时的代码位置信息。举例:index.js
> 1 const myObject = {}; > 2 Error.captureStackTrace(myObject); > 3 console.log(myObject.stack);
执行
node index.js
后,终端输出:Error at Object.<anonymous> (xxx/index.js:2:7) at Module._compile (internal/modules/cjs/loader.js:689:30) at ... at ...
当传入
constructorOpt
时,代码如:> 1 function MyError() { > 2 Error.captureStackTrace(this, MyError); > 3 } > 4 > 5 console.log(new MyError().stack)
终端输出:
Error at Object.<anonymous> (xxx/index.js:5:13) at Module._compile (internal/modules/cjs/loader.js:689:30) at ... at ...
可以看出,
MyError
函数内部的堆栈细节被隐藏了。 -
Error.captureStackTrace
优点相对于
new Error().stack
,Error.captureStackTrace
有以下优点:-
更简洁
无需new一个新的Error对象,节省内存空间,同时代码上也会更加优雅。
一般而言,捕获错误信息通常的做法是:
try { new Error(); } catch(err) { // err.stack 包含了堆栈信息,可以对其处理 }
而使用
Error.captureStackTrace
可以直接获取堆栈信息,实现方式更简洁。 -
更安全
如果需要忽略部分堆栈信息,使用Error.captureStackTrace会更加方便,无需手工操作。
-
更少资源
使用
Error.captureStackTrace
时,只有访问targetObject.stack
时,才会进行堆栈信息的格式化工作。如果
targetObj.stack
未被访问,则堆栈信息的格式化工作会被省略,从而节省计算资源。
-
-
Error.captureStackTrace
使用场景Error.captureStackTrace
并不是Node.js创造的,而是V8引擎的Stack Trace API。语法上,Node.js中的Error.captureStackTrace()
与V8引擎中所暴露的接口完全一致。事实上,Node.js的Error类中,所有与stack trace有关的内容均依赖于V8的Stack Trace API。
因此,
Error.captureStackTrace(targetObject[, constructorOpt])
使用的场景有:-
基于V8引擎的运行环境,如Node.js、Chrome浏览器
-
Error.captureStackTrace(this, MyError)
作用也是隐藏构造函数内部的堆栈信息,但需要明确指定构造函数名,通用性不强。
-
Error.captureStackTrace(this, arguments.callee)
arguments.callee
表示当前函数,也有通用性。但ES3及之后的严格模式禁用了arguments.callee
,因此不建议使用。 -
Error.captureStackTrace(this, this.constructor)
该做法可以隐藏构造函数内部的堆栈信息,无需指定构造函数名,通用型强。
-
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!