前端架构: 脚手架命令行交互核心实现之inquirer和readline的应用教程

命令行交互核心实现

  • 核心目标:实现命令行行交互,如List
  • 命令行的交互呢比命令行的渲难度要更大,因为它涉及的技术点会会更多
  • 它涉及以下技术点
    • 键盘输入的一个监听 (这里通过 readline来实现)
    • 计算命令行窗口的尺寸
    • 清屏
    • 光标的移动
    • 输出流的静默 (我们输出的内容, 不让它去输出到当前的这个终端中)
    • 借助输入输出流,引出输入输出流的一个监听以及事件库 events
    • ansi escaped code 转义字符
  • 命行交互其实是有一定复杂度的, 在这个过程中,最重点的库和命行交互最重点库是两个 readlineinquirer

inquirer

  • inquirer 是一个命令行交互常用的库,Weekly Downloads 30,375,340 (动态数据)

  • 作为一个命令行交互的库能做到这个程度,可以说是非常的不简单,而且一直在持续的进行维护,目前已经达到9.2.15版本了

  • 安装 $ npm i -S inquirer

  • 使用示例1:input类型演示

    js 复制代码
    import inquirer from 'inquirer';
    inquirer
      .prompt([
        {
          type: 'input',
          name: 'yourName',
          message: 'your name:',
        }
      ])
      .then((answers) => {
        console.log(answers);
      })
      .catch((error) => {
        if (error.isTtyError) {
          // Prompt couldn't be rendered in the current environment
        } else {
          // Something else went wrong
        }
      });


效果实例

  • 另外,比较常用的还有

    • default 默认值字段
    • validate 字段是一个回调
      • 用于对字段的校验,只有校验返回 true的时候校验才会结束
    • transformer 字段用于处理信息展示的回调
      • 也就是这个函数内部返回的值是展示的值
      • 返回的值还是之前的 name 字段
      • 更多的像是表单中的 placeholder 仅作为展示
    • filter 字段是一个回调
      • 它会最终改变 answers 最终的结果
      • 会最终改变 name 字段
  • 其他: choice 在匹配 List 列表的时候会用到

  • 注意,prompt 方法内部接受的是一个数组,可以写多个对象来收集数据

  • 使用示例2: 多字段演示

    js 复制代码
    import inquirer from 'inquirer';
    
    inquirer
      .prompt([
    	  {
    	  	type: 'input',
    	    name: 'yourName',
    	    message: 'your name:',
    	    default: 'Lee',
    	    validate: function(v) {
    	    	return v === 'Wang'
    	    },
    	    transformer: function(v) {
    	    	return 'your input name: ' + v // 仅作为展示
    	    },
    	    filter: function(v) {
    	    	return v;
    	    	// return v + '123' // 改变最终值
    	    }
    	  },
    	  {
    	  	type: 'number', // 这种,在没有 validate 的情况下,如果输入的是非数字, 会变成 NaN
    	  	name: 'num',
    	  	message: 'your number',
    	  },
    	  // ...
      ])
      .then((answers) => {
        console.log(answers); // 最终打印的是一个对象,多个字段
      })
      .catch((error) => {
        if (error.isTtyError) {
          // Prompt couldn't be rendered in the current environment
        } else {
          // Something else went wrong
        }
      });
    • 在示例1中已做了详细说明,这里不再赘述


效果实例

  • 使用示例3: confirm 类型演示

    js 复制代码
    import inquirer from 'inquirer';
    
    inquirer
      .prompt([
    	  {
    	  	type: 'confirm', // 二选一功能
    	    name: 'choice',
    	    message: 'your choice:',
    	    default: false,
    	  },
      ])
      .then((answers) => {
        console.log(answers);
      })
      .catch((error) => {
        if (error.isTtyError) {
          // Prompt couldn't be rendered in the current environment
        } else {
          // Something else went wrong
        }
      });


效果实例

  • 使用示例4: list 类型演示

    js 复制代码
    import inquirer from 'inquirer';
    
    inquirer
      .prompt([
    	  {
    	  	type: 'list', // 列表单选
    	    name: 'choice',
    	    message: 'your choice:',
    	    default: 0, // 这里 default 是 下面choices 的索引
    	    choices: [
    	    	{value: 1, name: 'LiLy'},
    	    	{value: 2, name: 'Lucy'},
    	    	{value: 3, name: 'Lee'},
    	    ]
    	  },
      ])
      .then((answers) => {
        console.log(answers);
      })
      .catch((error) => {
        if (error.isTtyError) {
          // Prompt couldn't be rendered in the current environment
        } else {
          // Something else went wrong
        }
      });


效果实例

  • 使用示例5: expend 类型演示

    js 复制代码
    import inquirer from 'inquirer';
    
    inquirer
      .prompt([
    	  {
    	  	type: 'expand', // 简写选择
    	    name: 'choice',
    	    message: 'your choice:',
    	    default: 'red',
    	    choices: [
    	    	{value: 'red', key: 'R'},
    	    	{value: 'green', key: 'G'},
    	    	{value: 'blue', key: 'B'},
    	    ]
    	  },
      ])
      .then((answers) => {
        console.log(answers);
      })
      .catch((error) => {
        if (error.isTtyError) {
          // Prompt couldn't be rendered in the current environment
        } else {
          // Something else went wrong
        }
      });
    • 简写选择功能,除了 Rgb 还有一个 h
    • 输入 h 回车,会得到 help 提示,列出了所有选项
    • 输入 r 回车,会得到 red, 输入 g 回车,会得到 green


效果实例

  • 使用示例6: checkbox 类型演示

    js 复制代码
    import inquirer from 'inquirer';
    
    inquirer
      .prompt([
    	  {
    	  	type: 'checkbox', // 复选框
    	    name: 'choice',
    	    message: 'your choice:',
    	    default: 0,
    	    choices: [
    	    	{value: 1, name: 'Lily'},
    	    	{value: 2, name: 'Lucy'},
    	    	{value: 3, name: 'Lee'},
    	    ]
    	  },
      ])
      .then((answers) => {
        console.log(answers);
      })
      .catch((error) => {
        if (error.isTtyError) {
          // Prompt couldn't be rendered in the current environment
        } else {
          // Something else went wrong
        }
      });
    • 这里提供 a 全选,空格键 选中,i 反选的功能
    • 默认,上下箭来选择


效果实例

  • 使用示例7: password 类型演示

    js 复制代码
    import inquirer from 'inquirer';
    
    inquirer
      .prompt([
    	  {
    	  	type: 'password', // 密码框
    	    name: 'password',
    	    message: 'your password:',
    	  },
      ])
      .then((answers) => {
        console.log(answers);
      })
      .catch((error) => {
        if (error.isTtyError) {
          // Prompt couldn't be rendered in the current environment
        } else {
          // Something else went wrong
        }
      });


效果实例

  • 使用示例8: editor 类型演示

    js 复制代码
    import inquirer from 'inquirer';
    
    inquirer
      .prompt([
    	  {
    	  	type: 'editor', // 编辑器
    	    name: 'editor',
    	    message: 'your editor text:',
    	  },
      ])
      .then((answers) => {
        console.log(answers);
      })
      .catch((error) => {
        if (error.isTtyError) {
          // Prompt couldn't be rendered in the current environment
        } else {
          // Something else went wrong
        }
      });


效果实例

  • 上面中间的这个类似 vim 的界面,会在一个缓存文件中,输入完以后,缓存文件被删除掉
  • 我们输入的结果会被保留下来,如上图
  • 这样做的好处是在文本编辑器中输入复杂的内容

readline

  • readline,是 nodejs 当中的一个内置库,主要帮我们去管理数据流的

  • 命令行当中要交互的方式,一定是需要用户提供一些输入的

  • readline 就可以很好的帮我们去一次一次的读取这个输入流

  • 注意,这个输入不仅是指我们输入一些字符,还包含我们键盘上输入的一切,如上,下,空格,回车等

  • 基本使用

    js 复制代码
    import * as readLine from 'readline';
    
    const rl = readLine.createInterface({
    	input: process.stdin,
    	output: process.stdout,
    });
    
    rl.question('your name: ', (answer) => {
    	console.log(answer);
    	rl.close(); // 关闭读取流
    })
  • readline 主要用途是根据传入的输入流逐行读取信息

  • 回车的时候,会认为这行输入结束,并且把所有输入的内容传递到输出流中进行展示

  • 这是readline的核心用途

  • 如果调试 readline 源码,可知,它内部会强制将函数转换为构造函数

    js 复制代码
    if (!(this instanceof Interface)) {
      return new Interface(input, output, completer, terminal);
    }
  • 接着是对 StringDecoder的判断和赋值,这个也是node的一个内置库

    js 复制代码
    if (StringDecoder === undefined) {
      StringDecoder = require('string_decoder').StringDecoder;
    }
  • 再之后,定义了一些列的参数,调用了 EventEmitter

    js 复制代码
    EventEmitter.call(this)
    • 这个用途是使用 this 继承 EventEmitter, this内部就会生成一些列的属性信息,如 _events, _eventsCount
    • 让当前 Interface 实例具备事件驱动的能力,因为nodejs有单线程,非阻塞IO,事件驱动的特性
    • 也就是说事件驱动,在单线程的nodejs中是非常重要的
  • 再接着,定义一些参数, 对 input 进行判断,也就是分析 input 参数

    js 复制代码
    if (input && input.input) {
      // ....
    }
  • 再往后找,看readline是如何做事件监听的

    js 复制代码
    this.output = output; // output: WriteStream 系统输出流
    this.input = input; // input: ReadStream 系统输入流
    
    // ...
    
    emitKeypressEvents(input, this) // 这里就是监听用户在终端中的键盘输入
    • 在 emitKeypressEvents 函数内部,会调用一个 emitKeys 的方法
    • 这里是核心, 其原理和源码不在这里进行剖析
相关推荐
临枫541几秒前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
前端每日三省2 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
小刺猬_9852 分钟前
(超详细)数组方法 ——— splice( )
前端·javascript·typescript
渊兮兮3 分钟前
Vue3 + TypeScript +动画,实现动态登陆页面
前端·javascript·css·typescript·动画
鑫宝Code4 分钟前
【TS】TypeScript中的接口(Interface):对象类型的强大工具
前端·javascript·typescript
爱吃青椒不爱吃西红柿‍️11 分钟前
华为ASP与CSP是什么?
服务器·前端·数据库
一棵开花的树,枝芽无限靠近你15 分钟前
【PPTist】添加PPT模版
前端·学习·编辑器·html
陈王卜17 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
景天科技苑25 分钟前
【vue3+vite】新一代vue脚手架工具vite,助力前端开发更快捷更高效
前端·javascript·vue.js·vite·vue项目·脚手架工具
SameX27 分钟前
HarmonyOS Next 安全生态构建与展望
前端·harmonyos