在JavaScript(Node)中进行输入
什么是Readline
Node.js中的readline模块提供了一个接口来读取用户输入和文件,并且能够以逐行的方式进行读取和处理。readline模块可以从标准输入流(stdin)中按行读取数据,也可以从一个可读流(Readable Stream)中读取数据。
输出我们都知道可以用console.log。
那你知道怎么在JavaScript(Node)中怎么进行输入吗?
我们可以使用readline模块来在终端读取我们输入的数据,使用如下:
- 导入readline模块
- 创建readline实例
- 调用相关的实例方法或者监听和处理readline事件
接下来,我们按照步骤,一步一步的在控制台中进行相关输入并读取。
导入Readline模块
首先,我们要导入readline这个模块,随着NodeJS的发展,这个模块的导入方式有非常多种,从包名字改变,到CJS到ESM的标准改变,接下来我们尝试各种形式的导入,我们分为CJS和ESM两种形式
CJS导入
const readline = require("readline");
Nodejs v16以上的文档,官方的案例用的都是node:readline
const readline = require("node:readline");
nodejs v17以上,开始有了promise api,所以有了node:readline/promise
const readline = require("node:readline/promise");
ESM导入
Node.js 12.0.0 版本开始正式支持 ECMAScript 模块(ESM)。在 Node.js 中,它们被称为 ES 模块或 ECMAScript 模块。在此之前,我们可以使用 CommonJS 规范来管理模块。从 Node.js 12 开始,我们可以通过修改文件扩展名(从 .js 到 .mjs)或者添加 "type":"module" 属性来使用 ES 模块。
凭借我们自己的感觉,应该这么写的,这样写确实能用。
import readline from "readline";
但是,官方文档都是这样写的🙃
import * as readline from "readline";
import * as readline from "node:readline";
import * as readline from "node:readline/promise";
创建一个接口实例
首先,我们要创建一个接口实例,提供一个Object类型的参数,这个参数有以下属性:
- input:监听的可读流(必需)
- output:写入readline的可写流(必需)
- completer:可选的用于制表符自动补全的函数。
process
input和output我们需要从process中引入,node全局中就有个process,但在最新的文档中,官方写的案例process都是从node:process包中引入的,这意思是可能以后全局中的process要被废弃???🤔,我们不得而知。下面开始创建接口实例
import * as readline from "node:readline";
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
})
官方文档的案例
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
const rl = readline.createInterface({ input, output });
单行输入
我们使用**rl.question(query, callback)** 来进行单行输入,rl.question()是Node.js中readline模块提供的方法,用于从控制台读取用户输入数据并返回结果。当调用 rl.question(query, callback)时,它会先输出一个问题(由query参数指定),等待用户在控制台上输入答案后,将答案作为字符串传递给回调函数(由callback参数指定)。
当然,其实用line事件,也可以进行单行输入,这个我们下面会讲到
接下来,我们写个案例来学习怎么使用,
先用我们常见的CJS模块化来编写
const readline = require("readline");
// 创建接口实例
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
rl.question("你的名字是? ", (name) => {
  console.log(`Hello ${name}!`);
  rl.close();
});
然后使用ESM,这是最新的官方文档中的写法,查阅:Node.js v19.8.1。
import * as readline from "node:readline";
import { stdin as input, stdout as output } from "node:process";
// 创建接口实例
const rl = readline.createInterface({ input, output });
rl.question("你的名字是? ", (name) => {
  console.log(`Hello ${name}!`);
  rl.close();
});
使用Promise Api,
import * as readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
// 创建接口实例
const rl = readline.createInterface({ input, output });
rl.question("你的名字是? ").then((name) => {
  console.log(`Hello ${name}!`);
  rl.close();
});
效果如下:

需要注意的是,我们在最后面一定要调用
rl.close()关闭readline.Interface实例并放弃对input和output流的控制。 当调用时,将触发'close'事件。
多行输入
line事件
多行输入我们一般采用line事件,每当 input 流接收到行尾输入(\n、\r 或 \r\n)时,则会触发 'line' 事件。 这通常发生在用户按下 回车 或 返回 时。
如果从流中读取了新数据并且该流在没有最终行尾标记的情况下结束,也会触发 'line' 事件。
使用如下:
rl.on('line', (input) => {
  console.log(`Received: ${input}`);
});
close事件
close事件一般用在我们结束输入时需要进行的操作,发生以下情况之一时会触发close事件:
- rl.close()方法被调用,- readline.Interface实例放弃了对- input和- output流的控制
- input流接收到它的- end事件;
- input流接收 Ctrl+D 以发出传输结束(EOT)的信号;
- input流接收 Ctrl+C 以发出- SIGINT信号,并且在- readline.Interface实例上没有注册- 'SIGINT'事件监听器。
使用如下:
rl.on("close",()=>{
  console.log("结束输入");
})
这里我们写一个案例:我们在控制台中连续输入名字,并把名字添加到names数组中,当我们结束输入时,打印names数组。
import * as readline from "node:readline/promise";
import { stdin as input, stdout as output } from "node:process";
// 创建接口实例
const rl = readline.createInterface({ input, output });
const names = [];
rl.on("line", (line) => {
  // 当我们想要结束输入时,可以输入'quit'
  // 这时,就会调用rl.close(), 结束输入,并触发close事件
  if (line === "quit") return rl.close();
	
  // 把我们从控制台输入的内容添加进names数组中
  names.push(line);
});
rl.on("close", () => {
  // 输入介绍后,打印names数组
  console.log(names);
});
效果如下:

我们可以根据自己需要,采用何种方式去触发rl.close()
rl.prompt([preserveCursor])
rl.prompt() 方法将配置为 prompt 的 readline.Interface 实例写入 output 中的新行,以便为用户提供用于提供输入的新位置。
当调用时,如果 rl.prompt() 流已暂停,则 rl.prompt() 将恢复 input 流。
如果 readline.Interface 是在 output 设置为 null 或 undefined 的情况下创建的,则不会写入提示。
简单来说,就是在我们每行的输入中,加个提示词
我们还是以之前输入名字案例来说,我们想要在用户开始输入时,加个提示词,我们可以这么做
首先,设置提示词
rl.setPrompt("请输入名字:");
在执行line事件前,调用rl.prompt()
rl.prompt();
最终代码如下:
const readline = require("readline");
// 创建接口实例
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
const names = [];
// 设置提示符
rl.setPrompt("请输入名字:");
// 在输入前,提前打印提示符
rl.prompt();
rl.on("line", (line) => {
  if (line === "quit") return rl.close();
  names.push(line);
  // 在下次输入前,答应提示符
  rl.prompt();
});
rl.on("close", () => {
  console.log(names);
});
效果如下:

有人会觉得prompt()有毛用啊,用起来还奇怪,还不如直接用console.log()呢,刚开始我也用这种感觉,但后来仔细一想,发现了区别:
rl.prompt()是不会换行的,但console.log()会换行
如果我们把前面的rl.prompt()换成console.log("请输入名字:"),就变成了下面这样

最后
创作不易,希望可以点个赞支持一下🥰🥰。
转载自:https://juejin.cn/post/7213665606629392421




