Inquirer.js:构建交互式命令行问答
Inquirer.js 入门与实战:构建交互式命令行问答
Inquirer.js 是 Node.js 生态中最流行的交互式命令行界面库,它能让你用几行代码就创建出美观、易用的问答流程,收集用户输入。无论是构建 CLI 工具、自动化脚本还是项目脚手架,Inquirer 都能大幅提升用户体验。
为什么选择 Inquirer?
- 丰富的交互类型:输入框、多选、确认、列表、密码等,覆盖绝大多数场景。
- 强大的校验与转换:内置验证、过滤、数据转换机制,确保输入合法。
- 简明的异步 API:基于 Promise,易于与现代异步流程集成。
- 高度可定制:支持动态问题、条件显示、自定义前缀与主题。
- 庞大的社区:GitHub 24k+ Stars,Vue CLI、Create React App 等知名项目都在使用。
安装与第一个问答
确保你的开发环境已安装 Node.js(12+),然后新建项目或直接在现有项目中安装:
npm install inquirer
创建一个 ask.js 文件,写入以下代码:
import inquirer from 'inquirer';
const questions = [
{
type: 'input',
name: 'username',
message: '请输入你的名字:',
default: '游客',
},
];
inquirer.prompt(questions).then((answers) => {
console.log(`你好,${answers.username}!`);
});
运行 node ask.js,你会看到一个带前缀的问句,输入后按回车即可。这就是 Inquirer 的核心工作流:定义问题数组,调用 prompt 方法,返回包含答案的对象。
问题对象基础
每个问题都是一个包含以下字段的对象(部分字段可选):
| 字段 | 类型 | 说明 |
|---|---|---|
type |
string |
问题类型,例如 'input', 'list', 'checkbox' 等 |
name |
string |
答案在结果对象中的键名 |
message |
string | Function |
显示给用户的提示信息,支持动态生成 |
default |
any | Function |
默认值,按回车直接使用 |
choices |
Array | Function |
用于 list, rawlist, expand, checkbox 等类型 |
validate |
Function |
输入验证函数 |
filter |
Function |
答案过滤/转换函数 |
when |
boolean | Function |
条件判断,决定该问题是否展示 |
问题类型详解
Inquirer 提供了多种内置类型,下面逐一介绍并给出示例。
1. input —— 文本输入
最基础的类型,用户可自由输入字符串。
{
type: 'input',
name: 'email',
message: '请输入邮箱:',
validate(value) {
const pass = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
return pass || '请输入有效的邮箱地址!';
},
}
2. number —— 数字输入
接受数字,自动转换类型,可配置边界。
{
type: 'number',
name: 'age',
message: '你的年龄是?',
validate(value) {
return (value >= 0 && value <= 120) || '年龄不合法';
},
default: 18,
}
3. confirm —— 确认询问
返回布尔值,常用于确认操作。
{
type: 'confirm',
name: 'continue',
message: '是否继续执行?',
default: true,
}
4. list —— 单选列表
用户通过上下箭头选择一项,返回选中值或对象。
{
type: 'list',
name: 'framework',
message: '选择前端框架:',
choices: [
{ name: 'React', value: 'react' },
{ name: 'Vue', value: 'vue' },
{ name: 'Angular', value: 'angular' },
new inquirer.Separator(), // 分隔线
{ name: '退出', value: 'quit' },
],
}
说明:choices 可以是字符串数组,也可以是包含 name(显示文字)和 value(实际值)的对象数组。Separator 用于添加不可选的分隔符。
5. rawlist —— 索引列表
与 list 类似,但显示为有序编号列表,用户输入数字选择。
{
type: 'rawlist',
name: 'editor',
message: '偏好编辑器:',
choices: ['VS Code', 'WebStorm', 'Sublime Text'],
}
6. expand —— 展开式菜单
展示一组快捷键选项,按对应字符选择。
{
type: 'expand',
name: 'theme',
message: '选择主题:',
choices: [
{ key: 'd', name: 'Dark', value: 'dark' },
{ key: 'l', name: 'Light', value: 'light' },
{ key: 'c', name: 'Custom', value: 'custom' },
],
}
7. checkbox —— 多选列表
用户可以勾选多个选项,返回所选值的数组。
{
type: 'checkbox',
name: 'features',
message: '选择需要的特性:',
choices: [
{ name: 'TypeScript', checked: true },
{ name: 'ESLint' },
{ name: 'Prettier', checked: true },
],
}
checked: true 可以使选项默认打勾。
8. password —— 密码输入
输入内容会被隐藏,常用于敏感信息。
{
type: 'password',
name: 'token',
message: '请输入 API Token:',
mask: '*', // 可自定义掩码
}
9. editor —— 编辑器输入
在外部编辑器(如 vim、nano)中打开临时文件供用户编辑。
{
type: 'editor',
name: 'bio',
message: '编辑你的简介:',
}
注意:需要系统具备默认编辑器或通过 EDITOR 环境变量指定。
验证、过滤与转换
良好的 CLI 工具必须保证数据质量,Inquirer 在问题定义中提供了三个核心函数:
validate:接收用户输入,返回true或错误信息字符串。filter:接收原始输入,返回处理后的值,不影响最终答案的类型。transformer:仅影响界面显示,不影响最终答案,可实时预处理展示内容。
示例:强制输入大写并验证长度。
{
type: 'input',
name: 'code',
message: '输入代码(至少6位):',
filter(input) {
return input.toUpperCase();
},
validate(input) {
return input.length >= 6 || '代码长度至少6位';
},
}
动态行为:when 与函数属性
很多属性可以是一个函数,根据之前的答案动态决定。
基于答案的条件显示
const questions = [
{
type: 'confirm',
name: 'wantTypes',
message: '需要 TypeScript 支持吗?',
},
{
type: 'list',
name: 'tsTarget',
message: '选择编译目标:',
choices: ['ES6', 'ESNext'],
when(answers) {
return answers.wantTypes; // 仅当用户选择 “是” 时才显示
},
},
];
动态生成默认值或选项
{
type: 'list',
name: 'packageManager',
message: '选择包管理器:',
choices: ['npm', 'yarn', 'pnpm'],
default(answers) {
// 如果用户之前选择了 Vue,则默认使用 yarn
return answers.framework === 'vue' ? 'yarn' : 'npm';
},
}
组合复杂流程
实际项目中,单个 prompt 往往不够,需要多步骤、循环或条件分支。你可以将多次 prompt 串联。
async function ask() {
const { env } = await inquirer.prompt({
type: 'list',
name: 'env',
message: '部署环境:',
choices: ['开发', '测试', '生产'],
});
if (env === '生产') {
const { confirm } = await inquirer.prompt({
type: 'confirm',
name: 'confirm',
message: '⚠️ 即将部署到生产环境,确认?',
});
if (!confirm) {
console.log('已取消');
return;
}
}
const details = await inquirer.prompt([
{ type: 'input', name: 'version', message: '版本号:' },
{ type: 'password', name: 'token', message: '部署 Token:' },
]);
console.log('最终配置:', { env, ...details });
}
ask();
美化输出:前缀、分隔符与样式
- 自定义前缀:在
prompt时传入prefix选项。 - 主题定制:通过
new inquirer.ui.Prompt(stream, options)深入控制,或借助@inquirer/select等新模块。
inquirer.prompt(questions, { prefix: '🚀' });
实战:构建一个项目初始化器
我们将模拟一个脚手架工具,引导用户选择模板、配置选项并生成项目。
import inquirer from 'inquirer';
async function initProject() {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: '项目名称:',
default: 'my-app',
validate: (v) => v.length > 0 || '名称不能为空',
},
{
type: 'list',
name: 'template',
message: '选择模板:',
choices: [
{ name: 'Vue 3 + Vite', value: 'vue-vite' },
{ name: 'React + Vite', value: 'react-vite' },
{ name: 'Node 后端', value: 'node-server' },
],
},
{
type: 'checkbox',
name: 'features',
message: '附加特性:',
choices: [
{ name: 'TypeScript', value: 'ts' },
{ name: 'ESLint + Prettier', value: 'lint' },
{ name: '单元测试 (Vitest)', value: 'test' },
],
},
{
type: 'input',
name: 'author',
message: '作者:',
default: 'dev',
},
{
type: 'confirm',
name: 'install',
message: '立即安装依赖?',
default: true,
},
]);
// 模拟生成操作
console.log('\n📁 项目配置已生成:');
console.log(JSON.stringify(answers, null, 2));
}
initProject();
至此,一个具备输入、选择、多选和确认的标准 CLI 向导就完成了。
进阶提示
- 搜索选择:使用
@inquirer/select(内置搜索)或inquirer-search-list在长列表中快速定位。 - 文件路径补全:通过
inquirer-file-tree-selection-prompt等插件实现。 - 无交互模式:用
inquirer的--silent或传入预设答案,方便测试。 - 错误处理:
prompt返回的 Promise 可以使用.catch捕获用户中断(Ctrl+C)。
总结
Inquirer.js 让命令行交互变得简单而强大。无论是简单的参数收集还是复杂的多步向导,你都可以通过组合问题类型、运用校验和条件逻辑来实现。掌握了这些技巧,你的 Node.js CLI 工具将具备专业级的用户体验。