- 开发工具
【免费下载链接】enquirer
Stylish, intuitive and user-friendly prompts. Used by eslint, webpack, yarn, pm2, pnpm, RedwoodJS, FactorJS, salesforce, Cypress, Google Lighthouse, Generate, tencent cloudbase, lint-staged, gluegun, hygen, hardhat, AWS Amplify, GitHub Actions Toolkit, @airbnb/nimbus, and more! Please follow Enquirer's author: https://github.com/jonschlinkert
Form Prompt 是 Enquirer 提供的多字段表单型提示器,它让用户可以在同一界面内依次填写多个字段,并支持字段导航、默认值、基于已有输入的动态建议等能力。本指南以 docs/prompts/form/getting-started.md 为主线,带你从最基础的input提示出发,逐步将其改造成包含多个字段的form,再深入默认值initial与onChoice回调的用法,并对照 lib/prompts/form.js 源码理解底层实现。读完本文,你将能够独立编写一个可运行、可扩展的多字段表单提示,并知道如何为每个字段注入默认值或联动逻辑。
适用场景:为什么在 CLI 工具中使用 Form
在构建命令行工具时,表单型提示(Form Prompt)非常有价值:它允许用户在一次交互中填写一组相关字段,例如用户注册信息、配置参数或调查问卷。与逐个弹出多个独立提示相比,Form 将多个字段呈现在同一块界面中,用户可以使用方向键在字段之间上下移动、逐字段输入,并随时回看和修改此前填写的内容。正如 docs/prompts/form/getting-started.md 所述,用户可以在输入过程中自由导航各字段,并得到多种便捷选项。
在源码层面,Form Prompt 由 lib/prompts/form.js 实现。它继承自SelectPrompt(lib/prompts/select.js),而SelectPrompt又继承自ArrayPrompt(lib/types/array.js),最终继承自基类Prompt(lib/prompt.js)。构造函数中通过super({ ...options, multiple: true })强制开启多选语义,并把this.type设置为'form'(lib/prompts/form.js),这意味着 Form 在内部复用了选择类提示的字段渲染、滚动与导航机制,只是把"选择项"变成了"可输入的字段"。
第一步:定义一个基础 Prompt
要使用 Form Prompt,首先从"enquirer"包中引入prompt方法,然后像定义普通提示一样定义一个输入字段。以下示例创建了一个名为firstname的文本输入(type: "input"):
const { prompt } = require("enquirer"); const results = prompt({ message: "First Name:", name: "firstname", type: "input" });这里的prompt是 Enquirer 的静态方法:在 index.js 中,Enquirer.prompt会创建一个新的Enquirer实例并依次处理传入的问题对象;而Enquirer.prompt上还会为每种已注册提示类型挂载快捷方法,例如Enquirer.prompt.form(index.js)。prompt()返回一个 Promise,其 resolve 值是一个以name为键、以用户输入为值的 answers 对象。
第二步:把 Prompt 升级为 Form
要把单个输入升级为表单,只需在配置对象中增加两个关键属性:
type: "form":声明这是一个表单提示;choices: string[] | Choice[]:声明表单中的字段列表。字段可以是纯字符串,也可以是包含message、name等属性的对象。
同时,把原先描述单个字段的message: string改为描述整个表单的引导语,并为整个表单指定一个独立的name: string(用于在 answers 对象中作为这一组字段的命名空间)。
const { prompt } = require("enquirer"); const results = prompt({ choices: [{ message: "First Name", name: "firstname" }], message: "Please provide the following information:", name: "user", type: "form" });值得注意的是,choices中的每个字段对象至少要给出name和message:name是提交结果对象中的键,message是界面中显示的字段标签。从源码看,lib/types/array.js 的toChoice()会对每个字段做归一化处理:ele.name = ele.name || ele.key || ele.title || ele.value || ele.message、ele.message = ele.message || ele.name,并初始化input = ''、cursor = 0等内部状态,因此即使只提供name也能正常渲染。
第三步:添加多个字段
现在可以继续为表单追加字段。把需要用户填写的每项信息都作为一个对象加入choices数组即可。下面是一个包含"名、姓、GitHub 用户名"三个字段的完整示例:
const { prompt } = require("enquirer"); const results = prompt({ choices: [ { message: "First Name", name: "firstname" }, { message: "Last Name", name: "lastname" }, { message: "GitHub username", name: "username" } ], message: "Please provide the following information:", name: "user", type: "form" });运行后,界面会依次呈现三个可输入字段,用户通过上下方向键切换焦点字段,直接键入字符即可填充,回车提交整个表单。提交后results形如:
{ user: { firstname: "Jon", lastname: "Schlinkert", username: "jonschlinkert" } }外层键user来自表单的name,内层键分别来自每个字段的name。这一结果的组装逻辑在 lib/prompts/form.js 的submit()中:提交时this.value = this.values,而this.values会在每次渲染字段时被重新填充(lib/prompts/form.js 的this.values[name] = (input || initial))。
运行示例与配套脚本
仓库的 guide/lib/prompts/form/getting-started.js 提供了与本文示例完全对应的可运行脚本,区别在于它用.then(...)/.catch(...)显式处理 Promise,并把结果经highlight高亮后打印到控制台:
const { prompt } = require('enquirer'); const response = prompt({ type: 'form', name: 'user', message: 'Please provide the following information:', choices: [ { message: 'First Name', name: 'firstname' }, { message: 'Last Name', name: 'lastname' }, { message: 'GitHub username', name: 'username' } ] }); response .then(answers => console.log(highlight(answers))) .catch(console.error);在本地安装依赖后(npm install),直接node guide/lib/prompts/form/getting-started.js即可体验完整的表单交互流程。
进阶一:用initial为字段设置默认值
在日常使用中,很多字段需要预填默认值。docs/prompts/form/default-values.md 介绍了最简单的方式:为字段对象添加initial: string属性。
const { prompt } = require("enquirer"); const results = prompt({ choices: [ { initial: "Jon", message: "First Name", name: "firstname" }, // ... ], // ... });initial的行为在源码中有两处体现:
- 占位提示:未输入时,lib/prompts/form.js 通过
placeholder(this, options)把initial作为占位文本显示在当前字段上,提示用户这是默认内容; - 自动补全:
next()方法(lib/prompts/form.js)在用户切换字段时检查:如果当前输入恰好是initial的前缀(initial.startsWith(input) && input !== initial),则自动把输入补全为完整的initial,并把光标移到末尾,从而支持"输入首字母即可快速补全默认值"的交互。
另外,initial还可以是函数(lib/types/array.js 会在字段初始化时调用ele.initial.call(this, this.state, ele, i)并把返回值写入ele.input),可用于异步或动态计算默认值。
进阶二:用onChoice复用已填字段
表单的常见需求是让后一个字段根据前面字段的输入自动生成建议值。例如,假设 GitHub 用户名等于(firstname + lastname).toLowerCase(),就可以在 username 字段上定义onChoice(state, choice, i)方法,从this.values中取出前面字段的值并动态设置choice.initial:
const { prompt } = require("enquirer"); const results = prompt({ choices: [ // ... { message: "GitHub username", name: "username", onChoice(state, choice, i) { const { firstname, lastname } = this.values; choice.initial = `${firstname}${lastname}`.toLowerCase(); } } ], // ... });onChoice的签名是onChoice(s: State, c: Choice, i: number)。它的调用链位于 lib/types/array.js:每当一个字段要渲染时,onChoice(choice, i)会先发出choice事件,若该字段定义了onChoice回调,则以this(即当前 Form 实例)为上下文调用,参数依次为this.state、choice、i;而 lib/prompts/form.js 的renderChoice()在每个字段渲染前都会调用this.onChoice(choice, i)。这保证了回调执行时,此前所有字段的值(含initial与已输入内容)已经写入this.values(见 lib/prompts/form.js),因此可以在回调中放心读取并覆盖当前字段的initial。
结合两种技巧,一个完整"带默认值与联动建议"的表单如下(对应 guide/lib/prompts/form/default-values.js):
const { prompt } = require("enquirer"); const results = prompt({ choices: [ { initial: "Jon", message: "First Name", name: "firstname" }, { initial: "Schlinkert", message: "Last Name", name: "lastname" }, { message: "GitHub username", name: "username", onChoice(state, choice, i) { const { firstname, lastname } = this.values; choice.initial = `${firstname}${lastname}`.toLowerCase(); } } ], message: "Please provide the following information:", name: "user", type: "form" });源码视角:Form 与 Select 的关键差异
从 lib/prompts/form.js 的实现细节可以看出 Form 之所以"像表单"的几处设计:
- 按键即输入:
dispatch(char)、space()、number()都会把字符追加到当前焦点字段(lib/prompts/form.js、L74-L80)。这与选择类提示中"空格切换选中"的语义完全不同——在 Form 中,空格和数字都成为字段内容的合法字符。 - 字段级光标编辑:
append、delete、deleteForward、left、right都在当前字段的input与cursor上操作(lib/prompts/form.js),使每个字段都像独立的小型输入框。 - 字段指示符:
indicator()对已有内容的字段显示⦿,对空字段显示⊙(lib/prompts/form.js),方便用户快速识别哪些字段尚未填写。 - 标签对齐:默认
align: 'right',字段名在渲染时通过padStart右对齐,使各字段的输入区对齐成一条竖线(lib/prompts/form.js、L136-L137)。 - 校验与提示:
renderChoice()会执行字段级validate(不通过则以 danger 色标红)、format(格式化显示)与result(改写最终值),并支持error/hint后缀提示(lib/prompts/form.js),后续可据此为单个字段添加独立校验。
下一步与相关文档
至此,你已经掌握了 Form Prompt 的基础用法:定义字段、切换字段、设置initial默认值,以及用onChoice实现字段间的联动建议。接下来可以继续深入:
- 为 Form Prompt 设置默认值(initial 与 onChoice 完整指南):本文进阶两节的完整出处,含更完整的可运行示例;
- guide/lib/prompts/form/getting-started.js 与 guide/lib/prompts/form/default-values.js:两份可直接运行的标准示例脚本;
- docs/prompts/form.md:Form Prompt 的其他特性与配置项概览;
- lib/prompts/form.js:Form 提示的完整源码,可进一步研究字段校验、格式化与提交流程。
……以上。Form Prompt 已就绪,接下来就看你如何把它组合进自己的 CLI 工具了。
- 开发工具
【免费下载链接】enquirer
Stylish, intuitive and user-friendly prompts. Used by eslint, webpack, yarn, pm2, pnpm, RedwoodJS, FactorJS, salesforce, Cypress, Google Lighthouse, Generate, tencent cloudbase, lint-staged, gluegun, hygen, hardhat, AWS Amplify, GitHub Actions Toolkit, @airbnb/nimbus, and more! Please follow Enquirer's author: https://github.com/jonschlinkert
相关推荐
vxe-table排序功能从入门到精通:单字段与多字段实战指南
vxe table排序功能从入门到精通:单字段与多字段实战指南 你是否还在为表格数据排序功能繁琐而烦恼?是否遇到过单字段排序无法满足复杂数据展示需求的情况?本文
前端UI组件react-jsonschema-form中的表单字段输入建议API设计
react jsonschema form中的表单字段输入建议API设计 在现代Web应用开发中,表单字段的输入建议功能(如自动补全、智能提示)是提升用户体验的
前端UI组件Gitea 源码构建指南:如何用 Makefile 快速编译并运行你的 Gitea
Gitea 源码构建指南:如何用 Makefile 快速编译并运行你的 Gitea Gitea 是一款轻量级、可自托管的 All in one 软件开发平台,集
后端代码托管研发协作CI/CD
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考