◆

Node.js inquirer 命令行交互提示用法详解

2023-02-02 01:370

写脚手架或者构建脚本时,比起让用户记一堆命令行参数,交互式地一问一答更友好——create-vue、vue-cli 那种「请选择要安装的功能」就是这么来的。inquirer 就是做这件事的库:定义一组问题,它负责渲染终端 UI、处理键盘操作,最后把所有答案汇总成一个对象返回。

它的设计是纯声明式的:一个问题就是一个配置对象,type 决定用哪种交互控件,name 决定答案对象里的键。真正让它好用的是几个通用字段——validate 做输入校验、filter 加工答案、when 按前面的回答决定这道题问不问、default 给默认值。有了 when 就能实现分支式的问答流程,而不是一根筋问到底。

下面这段把各类 type 和这几个字段都用了一遍,可以当查阅用的速查表。

inquirer

promptList 是问题数组,按类型分几组看:

  • input 自由文本输入。手机号那题用 validate 演示校验——返回 true 通过,返回字符串则把它当错误提示显示出来并要求重新输入(注释里给了正则校验位数的写法)。
  • confirm 是 y/n 二选一,答案是布尔值。prefix / suffix 是问题文案前后拼接的装饰内容。「是否进行文件过滤」那题的 when(answers) 是重点:answers 里是已经回答过的题,返回 false 这题就整个跳过——这里只有上一题选了监听才会继续问。
  • 三种单选各有适用场景:list 是方向键上下移动光标选;rawlist 给每项编号、输入数字选;expand 用 key 给每项配一个快捷字符,按键直接命中,适合选项多但常用项固定的场合。list 那题的 filter 演示了答案加工——把选中的 Apple 转成小写再存进结果。
  • checkbox 多选,答案是数组。choices 里可以传对象,用 checked: true 预选中;插入 new inquirer.Separator() 画分隔线,构造函数传字符串可以自定义分隔线文案。第二个 checkbox 用 pageSize: 2 限制一屏显示的行数,超出部分滚动显示,选项很多时避免刷满屏幕。
  • password 输入时不回显字符,editor 会调起系统默认编辑器(读 $EDITOR)写多行内容,适合填备注、commit message 这类长文本。

最后 inquirer.prompt(promptList) 返回 Promise,then 里的 answers 就是以各题 name 为键的答案对象。这里挂在一个 gulp task 上,实际用在 CLI 入口里是一样的写法。

const promptList = [
  {
    type: 'input',
    message: '设置一个用户名:',
    name: 'name',
    default: 'test_user', // 默认值
  },
  {
    type: 'input',
    message: '请输入手机号:',
    name: 'phone',
    validate(val) {
      // if (!val.match(/\d{11}/g)) { // 校验位数
      // 	return '请输入手机号';
      // }
      return true;
    },
  },
  {
    type: 'confirm',
    message: '是否使用监听?',
    name: 'watch',
    prefix: '前缀',
  },
  {
    type: 'confirm',
    message: '是否进行文件过滤?',
    name: 'filter',
    suffix: '后缀',
    when(answers) {
      // 当watch为true的时候才会提问当前问题
      return answers.watch;
    },
  },
  {
    type: 'list',
    message: '请选择一种水果:',
    name: 'fruit',
    choices: ['Apple', 'Pear', 'Banana'],
    filter(val) {
      // 使用filter将回答变为小写
      return val.toLowerCase();
    },
  },
  {
    type: 'rawlist',
    message: '请选择一种水果:',
    name: 'fruittwo',
    choices: ['Apple', 'Pear', 'Banana'],
  },
  {
    type: 'expand',
    message: '请选择一种水果:',
    name: 'fruitthree',
    choices: [
      {
        key: 'a',
        name: 'Apple',
        value: 'apple',
      },
      {
        key: 'O',
        name: 'Orange',
        value: 'orange',
      },
      {
        key: 'p',
        name: 'Pear',
        value: 'pear',
      },
    ],
  },
  {
    type: 'checkbox',
    message: '选择颜色:',
    name: 'color',
    choices: [
      {
        name: 'red',
      },
      new inquirer.Separator(), // 添加分隔符
      {
        name: 'blur',
        checked: true, // 默认选中
      },
      {
        name: 'green',
      },
      new inquirer.Separator('--- 分隔符 ---'), // 自定义分隔符
      {
        name: 'yellow',
      },
    ],
  },
  {
    type: 'checkbox',
    message: '选择颜色:',
    name: 'colorone',
    choices: ['red', 'blur', 'green', 'yellow'],
    pageSize: 2, // 设置行数
  },
  {
    type: 'password', // 密码为密文输入
    message: '请输入密码:',
    name: 'pwd',
  },
  {
    type: 'editor',
    message: '请输入备注:',
    name: 'editor',
  },
];

// 第一步
gulp.task(
  'initset',
  gulp.series(done => {
    inquirer.prompt(promptList).then(answers => {
      console.log(answers); // 返回的结果
    });
  }),
);

注意事项

  • inquirer 从 v9 开始是纯 ESM 包,require('inquirer') 会报错。gulpfile 还是 CommonJS 的项目要么锁在 v8,要么改用动态 import()。上面这段是 v8 及之前的写法(new inquirer.Separator() 这种用法也依赖旧版的 API 形态)。
  • 这个 task 里既没 return Promise 也没调 done(),gulp 判断不出任务结束,可能在用户答完题之前就退出了。规范写法是 return inquirer.prompt(...)。
  • validate 里要提示错误必须 return 一个字符串,直接 console.log 会被 inquirer 的 UI 重绘冲掉,看不到。
  • 交互式提问在 CI 里会直接卡死等输入。脚本要兼顾 CI,得先判断 process.stdout.isTTY,非交互环境下改从命令行参数或环境变量取值。
💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVENode.js inquirer 命令行交互提示用法详解加载中