最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • commander.js 原理解析

    正文概述 掘金(hoperyy)   2021-02-23   695

    原文发表在 Github: hoperyy blog

    概览

    commander.js 7.0.0版本的核心代码就一个文件index.js,2200多行代码,代码的注释比较丰富,代码可读性也是不错的,感兴趣的同学可以通读一下。

    commander.js包含以下类:

    • Option
    • Help
    • Command
    • CommandError
    • InvalidOptionArgumentError

    如下图:

    commander.js 原理解析

    Option类

    Options类的实例存储选项的各类信息:

    • 选项是否必填(requred)
    • 描述信息(description)
    • 可变参数(variadic)
    • 是否反向boolean,也就是--no-xx类型的选项
    • 长名称/短名称,也就是-c, --cheese
    • 参数的值
    • 其他

    commander.js 原理解析

    命令行实例执行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类使用时的主要流程:

    commander.js 原理解析

    我们简要介绍下其中的一些点:

    • 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类的继承关系。

    commander.js 原理解析

    在内部实现上,分别定义了每个类自身的特殊字段。但值得注意的是,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().stackError.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)

        该做法可以隐藏构造函数内部的堆栈信息,无需指定构造函数名,通用型强。


    起源地下载网 » commander.js 原理解析

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元