NodeJS 下构建 命令行工具 与 交互式命令界面 的实践

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: NodeJS 下构建命令行工具 与 交互式命令界面 的实践

NodeJS 下构建命令行工具 与 交互式命令界面 的实践


jcLee95 的个人博客

本文原发布于我的 CSDN博客。作者已入驻 阿里云社区。

未经作者授权禁止转载

邮箱 :291148484@163.com


本文地址:


【文章简介】:你想快速搭建 如 vue-cli、create-vue 那样的炫酷脚手架吗?本文将讲解以下内容:

Commander.JS是一个在NodeJS 环境下便捷地用于构建搞质量命令行工具的库,vue-cli 等很多命令行工具都是由它构建。本文详细讲解Commander.JS的相关概念,并通过案例介绍Commander.JS的使用方法。

inquirer.js 是一个实现交互式命令行界面的NodeJS库,文本还介绍了如何通过 inquirer.js 来实现交互式命令行界面。

此外,本文还介绍了一些其他的小工具,如:

  • kolorist ,在终端中使用色彩;
  • boxen,用于在终端中创建方框;

通过阅读本文,能够迅速帮助你掌握搭建脚手架的常用基本工具,让你拥有迅速搭建自己的专属脚手架的基本能力!


目 录


1. 概述

2. 命令的相关概念

3. 使用 Commander.js 搭建 命令工行工具

4. 使用 inquirer.js 构建 交互式命令界面

5. 终端中使用色彩模块 kolorist

6. 终端字符串样式模块: chalk

7. 同时用于 NodeJS 和 浏览器的控制台记录器:consola

8. 简单的CLI提示查询用户信息:prompts

9. 终端旋转亮片: ora

10. 终端渐变色字符串:gradient-string

11. 终端彩色文字动画: chalk-animation


1. 概述

Commander.js是一个在NodeJS 环境下便捷地用于构建搞质量命令行工具的库,vue-cli 等很多命令行工具都是由它构建。inquirer.js 是一个实现交互式命令行界面的 NodeJS 库,通过使用该库能够帮我们实现命令界面的交互式。kolorist 是一个

2. 命令的相关概念

3. 使用 Commander.js 搭建命令工行工具

3.1 安装

npm install commander
# 或者:
yarn add commander

3.2 引入 commander 的两种方式

方式1:

const { program } = require('commander');

方式2:

const { Command } = require('commander');
const program = new Command();

区别:

方式1中直接引入 commander 库中暴露(export)的变量 programCommand对象的实例,不需要再使用new来创建 Command对象的实例了,但对象名字只能是 program。

方式2中直接导入了 Command 对象,你需要手动创建它的实例:

const program = new Command();

这种方法下当然你可以将 Command 对象命名为其他的合法标识符,而不一定是program

3.3 从案例入门

3.3.1 例 1

777777777777777.gif

1. 新建项目

新建一个 NodeJS 项目,默认初始化项目信息:

npm init -y

2. 安装commander.js

npm install commander

3. 编写脚本

建立文件eg1.js,输入以下内容:

// eg1.js
const { Command } = require('commander');
const program = new Command();
program
  .name('字符串工具')
  .description('一些JavaScript字符串实用程序的 命令行工具')
  .version('0.0.1');
program.command('split')
  .description('将字符串拆分成子字符串,并显示为数组')
  .argument('<string>', '要拆分的字符串')
  .option('--first', '仅显示第一个子字符串')
  .option('-s, --separator <char>', '分隔符字符', ',')
  .action((str, options) => {
    const limit = options.first ? 1 : undefined;
    console.log(str.split(options.separator, limit));
});
program.parse();

4. 运行脚本

现在在该项目的根目录下打开终端。在终端中测试我们的命令行工具脚本。

(1)获取你的 CLI tool 版本
node .\eg1.js -V

Out[]:

0.0.1

【评注】:可以看到,这个版本号就是上面代码块第8行.version('0.0.1');所指定的版本号。

(2)获取CLI模块级帮助信息
node .\eg1.js -h

Out[]:

Usage: 字符串工具 [options] [command]
一些JavaScript字符串实用程序的 命令行工具
Options:
  -V, --version             output the version number
  -h, --help                display help for command
Commands:
  split [options] <string>  将字符串拆分成子字符串,并显示为数组
  help [command]            display help for command

【评注】:可以看到,当前你的 CLI 模块一共有两个选项,分别是 -V-h 这是模块级选项,不需要你手动定义,由 commander.js 内置提供。除了你自定义的命令split 外,还有一个名为help的命令,它同样是由 commander.js 内置提供的。

(3)获取该 CLI 模块中某个命令的帮助信息
node .\eg1.js split -h

Out[]:

Usage: 字符串工具 split [options] <string>
将字符串拆分成子字符串,并显示为数组
Arguments:
  string                  要拆分的字符串
Options:
  --first                 仅显示第一个子字符串
  -s, --separator <char>  分隔符字符 (default: ",")
  -h, --help              display help for command
(4)测试你编写的命令

在脚本中,我们通过 .command(’split‘) 定义了一个名为split的命令。

下面这个例子使用空格 作为分隔符:

node .\eg1.js split --separator=" " "hello, my name is Jack!"

Out[]:

[ 'hello,', 'my', 'name', 'is', 'Jack!' ]

下面这个例子指点逗号(,)作为分隔符:

node .\eg1.js split --separator="," "hello, my name is Jack!"

Out[]:

[ 'hello', ' my name is Jack!' ]

以下是在 powershell 窗口实际运行的截图:

6666666666666.png

5. 归纳1:声明命令

3.2 小节中,我们介绍了引入commander的两种方式,默认情况下,使用program作为实例对象的变量名。

在本例(【例1】) 我们使用program构建了我们的第一个命令行工具。可以看到,这个例子中唯一的自定义命令split就是通过program.command()声明的的。

变量 program 是一个 Command 实例,而command()是其下的一个实例方法,仍返回的Command:

Command.command(nameAndArgs: string, opts?: CommandOptions | undefined): Command

其详细的接口描述如下:

command(nameAndArgs: string, opts?: CommandOptions): ReturnType<this['createCommand']>;
  /**
   * 定义一个命令,在单独的可执行文件中实现。
   *
   * @remarks
   * 命令描述作为第二个参数提供给 `.command`.
   *
   * @param nameAndArgs - 命令名和参数, args是 `<required>` 或者 `[optional]` ,last也可以是`variadic...`
   * @param description - 可执行命令的描述
   * @param opts - 配置选项
   * @returns 用于链接的 “this” 的 command 
   */

例如:

program
  .command('start <service>', 'start named service')
  .command('stop [service]', 'stop named service, or all if no name supplied');

该方法还有一个重载

command(nameAndArgs: string, description: string, opts?: ExecutableCommandOptions): this;
  /**
   * 创建新的独立 command 的工厂例程。
   *
   * 有关创建附加子命令的信息,请参见`.command()',该子命令使用此例程来创建命令。您可以覆盖 createCommand 来定制子命令。
   */

6. 归纳2:声明选项


         

7. 归纳3:选项 类型 和 默认值


         

8. 拓展1:取反选项


         

9. 拓展2:可选参数的选项


         

10. 归纳4:处理函数


         

11. 归纳5:其它细节

(1)工具名称

通过.name()方法可以用于声明你的命令行工具的版本号,例如在本例中:

program
  .name('字符串工具')

这样以后,我们在帮助文档中就可以看到你的命令行工具最开始的

Usage: 字符串工具 [options] [command]
(3)版本选项

通过.version()方法可以用于声明你的命令行工具的版本号,用户在使用时,通过 3-3-1-4-1 小节 中的方法来来确定自己使用的工具脚本。例如:

program
  .version('0.0.1');

表示当前的版本号为0.0.1

3.4 进阶

这部分当前正在编辑中

3.4.1 补充

3.4.2 生命周期钩子

3.4.3 自动化帮助信息

3.5 接口声明

更多功能以及详细的接口语法格式,建议仔细阅读接口声明文件。在本章节中,给出了主要功能类的中文接口声明。

3.5.1 Command 类 接口声明

export class Command {
  args: string[];
  processedArgs: any[];
  commands: Command[];
  parent: Command | null;
  constructor(name?: string);
  /**
   * 将程序版本设置为 `str`
   *
   * 该方法自动注册 "-V, --version" 标志,当传递时,该标志将打印版本号。
   *
   * 您可以选择提供标志和描述来覆盖默认值。
   */
  version(str: string, flags?: string, description?: string): this;
  /**
   * 定义一个命令,使用 action 处理程序实现。
   *
   * @remarks
   * 命令描述是使用 `.description` 提供的,而不是作为 `.command` 的参数。
   *
   * @example
   * ```ts
   * program
   *   .command('clone <source> [destination]')
   *   .description('clone a repository into a newly created directory')
   *   .action((source, destination) => {
   *     console.log('clone command called');
   *   });
   * ```
   *
   * @param nameAndArgs - 命令名和参数,args 是  `<required>` 或者 `[optional]` 和 last 也可以是 `variadic...`
   * @param opts - 配置选项
   * @returns new command
   */
  command(nameAndArgs: string, opts?: CommandOptions): ReturnType<this['createCommand']>;
  /**
   * 定义一个命令,在单独的可执行文件中实现。
   *
   * @remarks
   * 命令描述作为第二个参数提供给 `.command`。
   *
   * @example
   * ```ts
   *  program
   *    .command('start <service>', 'start named service')
   *    .command('stop [service]', 'stop named service, or all if no name supplied');
   * ```
   *
   * @param nameAndArgs - 命令名和参数,args 是 `<required>` 或者 `[optional]` 和 last 也可以是 `variadic...`
   * @param description - 可执行命令的描述
   * @param opts - 配置选项
   * @returns 用于链接的 `this` 命令
   */
  command(nameAndArgs: string, description: string, opts?: ExecutableCommandOptions): this;
  /**
   * 创建新的独立命令的工厂例程。
   *
   * 有关创建附加子命令的信息,请参见`.command()`,该子命令使用此例程来创建命令。您可以覆盖createCommand来定制子命令。
   */
  createCommand(name?: string): Command;
  /**
   * 添加准备好的子命令。
   *
   * 有关创建从其父命令继承设置的附加子命令,请参见 `.command()`。
   *
   * @returns 用于链接的 `this` 命令
   */
  addCommand(cmd: Command, opts?: CommandOptions): this;
  /**
   * 创建新的独立参数的工厂例程。
   *
   * 有关创建附加参数的信息,请参见`.argument()`,它使用此例程来创建参数。您可以重写 createArgument 以返回自定义参数。
   */
  createArgument(name: string, description?: string): Argument;
  /**
   * 定义命令的参数语法。
   *
   * 默认情况下,参数是必需的,您可以在名称前后使用<>来明确指出这一点。
   * 在可选参数的名称两边加上[]。
   *
   * @example
   * ```
   * program.argument('<input-file>');
   * program.argument('[output-file]');
   * ```
   *
   * @returns 用于链接的 `this` 命令
   */
  argument<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
  argument(name: string, description?: string, defaultValue?: unknown): this;
  /**
   * 定义命令的参数语法,添加准备好的参数。
   *
   * @returns 用于链接的 `this` 命令
   */
  addArgument(arg: Argument): this;
  /**
   * 定义命令的参数语法,一次添加多个(不带描述)。
   *
   * See also .argument().
   *
   * @example
   * ```
   * program.arguments('<cmd> [env]');
   * ```
   *
   * @returns 用于链接的 `this` 命令
   */
  arguments(names: string): this;
  /**
   * 覆盖是否添加隐式帮助命令的默认决定。
   *
   * @example
   * ```
   * addHelpCommand() // 强制打开
   * addHelpCommand(false); // 强制关闭
   * addHelpCommand('help [cmd]', 'display help for [cmd]'); // 强制使用自定义细节
   * ```
   *
   * @returns 用于链接的 `this` 命令
   */
  addHelpCommand(enableOrNameAndArgs?: string | boolean, description?: string): this;
  /**
   * 为生命周期事件添加挂钩。
   */
  hook(event: HookEvent, listener: (thisCommand: Command, actionCommand: Command) => void | Promise<void>): this;
  /**
   * 注册回调以替换调用 process.exit。
   */
  exitOverride(callback?: (err: CommanderError) => never | void): this;
  /**
   * 显示错误信息并退出(或调用exitOverride)。
   */
  error(message: string, errorOptions?: ErrorOptions): never;
  /**
   * 您可以通过覆盖 createhelp 或使用 “configureHelp()” 覆盖帮助属性,用Help的子类自定义帮助。
   */
  createHelp(): Help;
  /**
   * 您可以通过使用configureHelp()覆盖帮助属性来自定义帮助,或者通过覆盖createHelp()使用帮助的子类。
   */
  configureHelp(configuration: HelpConfiguration): this;
  /** 获取配置 */
  configureHelp(): HelpConfiguration;
  /**
   * 默认输出到stdout和stderr。您可以为特殊应用定制此功能。
   * 您还可以通过覆盖outputError来自定义错误的显示。
   *
   * 配置属性都是函数:
   * ```
   * // 用于更改写入位置的函数,stdout 和 stderr
   * writeOut(str)
   * writeErr(str)
   * // 用于指定帮助换行宽度的匹配函数
   * getOutHelpWidth()
   * getErrHelpWidth()
   * // functions based on what is being written out
   * outputError(str, write) // 用于显示错误,不用于显示帮助
   * ```
   */
  configureOutput(configuration: OutputConfiguration): this;
  /** 获取配置 */
  configureOutput(): OutputConfiguration;
  /**
   * 复制在 根命令 和 子命令 之间通用的有用设置。
   *
   * (使用 `.command()' 添加命令时在内部使用,以便子命令继承父设置。)
   */
  copyInheritedSettings(sourceCommand: Command): this;
  /**
   * 出现错误后显示帮助或自定义消息。
   */
  showHelpAfterError(displayHelp?: boolean | string): this;
  /**
   * 为未知命令显示类似命令的建议,或为未知选项显示选项。
   */
  showSuggestionAfterError(displaySuggestion?: boolean): this;
  /**
   * 为命令注册回调 `fn`。
   *
   * @example
   * ```
   * program
   *   .command('serve')
   *   .description('start service')
   *   .action(function() {
   *     // do work here
   *   });
   * ```
   *
   * @returns 用于链接的 `this` 命令
   */
  action(fn: (...args: any[]) => void | Promise<void>): this;
  /**
   * 用 `flags`、`description`和可选强制`fn`定义选项。
   *
   * `flags` 字符串包含短标志和/或长标志,由逗号、竖线或空格分隔。当使用 `-help' 时,以下都是有效的输出。
   *
   *     "-p, --pepper"
   *     "-p|--pepper"
   *     "-p --pepper"
   *
   * @example
   * ```
   * // 简单boolean默认为false
   *  program.option('-p, --pepper', 'add pepper');
   *
   *  --pepper
   *  program.pepper
   *  // => Boolean
   *
   *  // 简单boolean默认为false
   *  program.option('-C, --no-cheese', 'remove cheese');
   *
   *  program.cheese
   *  // => true
   *
   *  --no-cheese
   *  program.cheese
   *  // => false
   *
   *  // 必需的参数
   *  program.option('-C, --chdir <path>', 'change the working directory');
   *
   *  --chdir /tmp
   *  program.chdir
   *  // => "/tmp"
   *
   *  // 可选参数
   *  program.option('-c, --cheese [type]', 'add cheese [marble]');
   * ```
   *
   * @returns 用于链接的 `this` 命令
   */
  option(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
  option<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
  /** @deprecated 从v7开始,改为使用 `choices` 或自定义功能 */
  option(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
  /**
   * 定义一个必需的选项,该选项在解析后必须有一个值。这通常意味着必须在命令行上指定该选项。 (否则与 `.option()` 相同。)
   *
   * `flags` 字符串包含 短标志 和/或 长标志,由逗号、竖线或空格分隔。
   */
  requiredOption(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
  requiredOption<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
  /** @deprecated 从v7开始,改为使用 `choices` 或自定义功能 */
  requiredOption(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
  /**
   * 创建新的独立选项的工厂例程。
   *
   * 有关创建附加选项的信息,请参见 `.option()`,它使用此例程来创建选项。您可以重写 createOption 以返回自定义选项。
   */
  createOption(flags: string, description?: string): Option;
  /**
   * 添加一个准备好的选项。
   *
   * 有关在单个调用中创建和附加选项的信息,请参见 `.option()` 和 `.requiredOption()`。
   */
  addOption(option: Option): this;
  /**
   * 是将选项值作为属性存储在命令对象上,还是单独存储(指定false)。在这两种情况下,都可以使用 `.opts()` 来访问选项值。
   *
   * @returns 用于链接的 `this` 命令
   */
  storeOptionsAsProperties<T extends OptionValues>(): this & T;
  storeOptionsAsProperties<T extends OptionValues>(storeAsProperties: true): this & T;
  storeOptionsAsProperties(storeAsProperties?: boolean): this;
  /**
   * 检索选项值。
   */
  getOptionValue(key: string): any;
  /**
   * 存储选项值。
   */
  setOptionValue(key: string, value: unknown): this;
  /**
   * 存储选项值以及该值的来源。
   */
  setOptionValueWithSource(key: string, value: unknown, source: OptionValueSource): this;
  /**
   * 检索选项值源。
   */
  getOptionValueSource(key: string): OptionValueSource;
  /**
   * 用可选值改变短标志的解析。
   *
   * @example
   * ```
   * // for `.option('-f,--flag [value]'):
   * .combineFlagAndOptionalValue(true)  // `-f80` 被视为 `--flag=80`, 这是默认行为
   * .combineFlagAndOptionalValue(false) // `-fb` 被视为 `-f -b`
   * ```
   *
   * @returns 用于链接的 `this` 命令
   */
  combineFlagAndOptionalValue(combine?: boolean): this;
  /**
   * 允许命令行上的未知选项。
   *
   * @returns 用于链接的 `this` 命令
   */
  allowUnknownOption(allowUnknown?: boolean): this;
  /**
   * 允许命令行上有过多的命令参数。传递false使多余的参数成为错误。
   *
   * @returns 用于链接的 `this` 命令
   */
  allowExcessArguments(allowExcess?: boolean): this;
  /**
   * 启用位置选项。位置意味着在子命令之前指定全局选项,这允许子命令重用相同的选项名,也允许子命令打开 passThroughOptions 。
   *
   * 默认行为是非定位的,全局选项可能出现在命令行的任何地方。
   *
   * @returns 用于链接的 `this` 命令
   */
  enablePositionalOptions(positional?: boolean): this;
  /**
   * 传递命令参数之后的选项,而不是将它们视为命令选项,
   * 因此实际的命令选项在命令参数之前。
   * 为子命令启用此选项需要在程序(父命令)上启用位置选项。
   *
   * 默认行为是非定位的,选项可能出现在命令参数之前或之后。
   *
   * @returns 用于链接的 `this` 命令
   */
  passThroughOptions(passThrough?: boolean): this;
  /**
   * 解析 `argv`,设置选项并在定义时调用命令。
   *
   * 默认情况下,参数来自 node,应用程序为 argv[0],脚本在 argv[1] 中运行,其后是用户参数。
   *
   * @example
   * ```
   * program.parse(process.argv);
   * program.parse(); // 隐式使用 process.argv 和自动检测 node vs electron conventions
   * program.parse(my-args, { from: 'user' }); // 只是用户提供的参数,argv[0]没有什么特别的
   * ```
   *
   * @returns 用于链接的 `this` 命令
   */
  parse(argv?: readonly string[], options?: ParseOptions): this;
  /**
   * 解析 `argv`,设置选项并在定义时调用命令。
   *
   * 如果您的任何操作处理程序是异步的,请使用 parseAsync 而不是 parse。 返回一个 Promise.
   *
   * 默认情况下,参数来自 node,应用程序为 argv[0],脚本在 argv[1] 中运行,其后是用户参数。
   *
   * @example
   * ```
   * program.parseAsync(process.argv);
   * program.parseAsync(); // 隐式使用 process.argv 和自动检测 node vs electron conventions
   * program.parseAsync(my-args, { from: 'user' }); // 只是用户提供的参数,argv[0]没有什么特别的
   * ```
   *
   * @returns Promise
   */
  parseAsync(argv?: readonly string[], options?: ParseOptions): Promise<this>;
  /**
   * 从 `argv` 中解析选项,删除已知选项,并返回argv拆分为操作数和未知参数。
   *
   *     argv => operands, unknown
   *     --known kkk op => [op], []
   *     op --known kkk => [op], []
   *     sub --unknown uuu op => [sub], [--unknown uuu op]
   *     sub -- --unknown uuu op => [sub --unknown uuu op], []
   */
  parseOptions(argv: string[]): ParseOptionsResult;
  /**
   * 返回一个包含作为键值对的本地选项值的对象
   */
  opts<T extends OptionValues>(): T;
  /**
   * 返回一个对象,该对象包含作为键值对的合并的本地和全局选项值。
   */
  optsWithGlobals<T extends OptionValues>(): T;
  /**
   * 设置描述。
   *
   * @returns 用于链接的 `this` 命令
   */
  description(str: string): this;
  /** @deprecated 从v8开始,改为使用 .argument 来添加带有描述的命令参数 */
  description(str: string, argsDescription: {[argName: string]: string}): this;
  /**
   * 获取描述。
   */
  description(): string;
  /**
   * 设置摘要。当作为父命令的子命令列出时使用。
   *
   * @returns 用于链接的 `this` 命令
   */
  summary(str: string): this;
  /**
   * 获取摘要。
   */
  summary(): string;
  /**
   * 为命令设置别名。
   *
   * 您可以多次调用以添加多个别名。自动生成的帮助中只显示第一个别名。
   *
   * @returns 用于链接的 `this` 命令
   */
  alias(alias: string): this;
  /**
   * 获取命令的别名。
   */
  alias(): string;
  /**
   * 为命令设置别名。
   *
   * 自动生成的帮助中只显示第一个别名。
   *
   * @returns 用于链接的 `this` 命令
   */
  aliases(aliases: readonly string[]): this;
  /**
   * 获取命令的别名。
   */
  aliases(): string[];
  /**
   * 设置命令用法。
   *
   * @returns 用于链接的 `this` 命令
   */
  usage(str: string): this;
  /**
   * 获取命令用法。
   */
  usage(): string;
  /**
   * 设置命令的名称。
   *
   * @returns 用于链接的 `this` 命令
   */
  name(str: string): this;
  /**
   * 获取命令的名称。
   */
  name(): string;
  /**
   * 从脚本文件名设置命令的名称,例如 process.argv[1]、require.main.filename 或 __filename。
   *
   * (虽然自述文件中没有记录,但在内部和公共场合使用。)
   *
   * @example
   * ```ts
   * program.nameFromFilename(require.main.filename);
   * ```
   *
   * @returns 用于链接的 `this` 命令
   */
  nameFromFilename(filename: string): this;
  /**
   * 设置搜索此命令的可执行子命令的目录。
   *
   * @example
   * ```ts
   * program.executableDir(__dirname);
   * // 或者
   * program.executableDir('subcommands');
   * ```
   *
   * @returns 用于链接的 `this` 命令
   */
  executableDir(path: string): this;
  /**
   * 获取可执行文件搜索目录。
   */
  executableDir(): string;
  /**
   * 输出此命令的帮助信息。
   *
   * 输出内置帮助和使用 `.addHelpText()` 添加的自定义文本。
   *
   */
  outputHelp(context?: HelpContext): void;
  /** @deprecated since v7 */
  outputHelp(cb?: (str: string) => string): void;
  /**
   * 返回命令帮助文档。
   */
  helpInformation(context?: HelpContext): string;
  /**
   * 您可以传入标志和描述来覆盖命令的帮助标志和帮助描述。
   * 传入false以禁用内置帮助选项。
   */
  helpOption(flags?: string | boolean, description?: string): this;
  /**
   * 输出帮助信息并退出。
   *
   * 输出内置帮助和使用 `.addHelpText()` 添加的自定义文本。
   */
  help(context?: HelpContext): never;
  /** @deprecated since v7 */
  help(cb?: (str: string) => string): never;
  /**
   * 添加要与内置帮助一起显示的附加文本。
   *
   * Position为 `before` 或 `after` 时仅影响该命令,而 `beforeAll` 或 `afterAll` 时影响该命令及其所有子命令。
   */
  addHelpText(position: AddHelpTextPosition, text: string): this;
  addHelpText(position: AddHelpTextPosition, text: (context: AddHelpTextContext) => string): this;
  /**
   * 添加事件发生时的侦听器(回调)。(使用EventEmitter实现。)
   */
  on(event: string | symbol, listener: (...args: any[]) => void): this;
}

4. 使用 inquirer.js 构建交互式命令界面

当你使用 vue-cli (现在以被create-vue取代,但仍使用 inquirer )等脚手架创建 vue 项目时,一定见到过很炫酷的交互式问答,以让脚手架了解到你的项目需求,比如是否需要使用 vue-routervuex(已被pinia替代),是否使用 CSS 预处理器(如lesssass),等等。这样的交互式问答功能就是由 使用 inquirer.js 所提供支持的。

当你自己搭建脚手架项目时,如果你的脚手架也要动态地了解用户本次执行的需求,那么使用 inquirer.js 是相当快捷、易于上手的选择。本章的任务就是学习和掌握如何使用 inquirer.js 来构造我们自己的一个交互式命令界面。

4.1 安装

npm install inquirer
# 或者
yarn add inquirer

4.2 从一个示例开始

4.2.1 创建示例项目

在项目下安装依赖:

npm install --save inquirer@^8.0.0

新建文件 eg3.js (如果你喜欢也可以用其它文件名),并添加一下内容:

// eg3.js
const inquirer = require('inquirer');
console.log('你好,欢迎体验 inquirer 命令行交互界面!');
console.log('下面我们将问你一些问题。。。');
const questions = [
  {
    type: 'confirm',
    name: 'toBeDelivered',
    message: '今天天气好吗?(Y:好,N:不好)',
    default: true,
  },
  {
    type: 'rawlist',
    name: 'select_number',
    message: '您是先生还是女士?(Y:先生,N:女士)',
    choices: ['先生', '女士'],
  },
  {
    type: 'input',
    name: 'CSDN_name',
    message: "您的CSDN用户名是?",
  },
  {
    type: 'input',
    name: 'phone',
    message: "您的手机号码是?",
    validate(value) {
      const pass = value.match(
        /^([01]{1})?[-.\s]?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})\s?((?:#|ext\.?\s?|x\.?\s?){1}(?:\d+)?)?$/i
      );
      if (pass) {
        return true;
      }
      return '请输入有效的电话号码!!';
    },
  },
  {
    type: 'list',
    name: 'fv_food',
    message: '您喜欢的食物类型是?',
    choices: ['甜食', '辛辣', '烧烤', '我不挑食'],
  },
  {
    type: 'expand',
    name: 'select_key',
    message: '请依据 key 指定一个选项:',
    choices: [
      {
        key: 'a',
        name: '选项1',
        value: 'A',
      },
      {
        key: 'b',
        name: '选项2',
        value: 'B',
      },
      {
        key: 'c',
        name: '选项3',
        value: 'C',
      },
    ],
  },
  {
    type: 'rawlist',
    name: 'select_number',
    message: '选择一个项目对应的编号',
    choices: ['第一', '第二', '第三'],
  },
  {
    type: 'input',
    name: '评论',
    message: '输入一段话',
    default: '这是默认输入的一段话。',
  },
];
inquirer.prompt(questions).then((answers) => {
  console.log('\n本次对话交互的信息如下:');
  console.log(JSON.stringify(answers, null, '  '));
});

运行效果如下:

777777777777777.gif

4.2.2 Inquirer 的导入方法

Inquirer v9 及更高版本是本机 esm 模块,这意味着您不能再使用 commonjs 语法require(‘inquirer’)。如果像本例中一样,使用传统 commonjs 语法,则需要像上面这样安装 Inquirer v8,否则需要使用import inquirer from 'inquirer'的写法。比如本例使用:

npm install --save inquirer@^8.0.0
# 或者
yarn add inquirer@^8.0.0

则对应使用 commonjs 语法导入 inquirer:

const inquirer = require('inquirer');

而安装 Inquirer v9 之后的版本则可以直接安装,就像靠头那样:

npm install inquirer
# 或者
yarn add inquirer

导入语法也对应换成 ES module的语法:

import inquirer from 'inquirer'

4.2.3 Inquirer 问题 与 question 对象

启动提示界面(查询会话)

inquirer.prompt(questions, answers) -> promise

4.2.4 Inquirer 回答(Answers)

4.2.5 Inquirer 分隔符(Separator)

4.2.6 Inquirer 问题提示类型(Separator)

(1)列表 list

例如:

{
  type: 'list',
  name: 'fv_food',
  message: '您喜欢的食物类型是?',
  choices: ['甜食', '辛辣', '烧烤', '我不挑食'],
},

(2)原始列表 rawlist


         

(3)扩展 expand


         

(4)选择框 checkbox

以下是一个使用了 checkbox 类型的完整示范:

const inquirer = require('inquirer');
inquirer
  .prompt([
    {
      type: 'checkbox',
      message: '选择框',
      name: 'ck',
      choices: [
        { name: 'option1' },
        { name: 'option2'},
        new inquirer.Separator('--- 下面的 option3 指定了默认被选中: ---'),
        {
          name: 'option3',
          checked: true,
        },
        new inquirer.Separator('--- 下面的 option4 是一个被禁用的选项: ---'),
        {
          name: 'option4',
          disabled: '禁用的选项',
        },
        { name: 'option5'},
      ],
      validate(answer) {
        if (answer.length < 1) {
          return '您必须选择至少一个选项。';
        }
        return true;
      },
    },
  ])
  .then((answers) => {
    console.log("以下是你的选择结果:");
    console.log(JSON.stringify(answers, null, '  '));
  });

其效果如下:

777777777777777.gif

(5)确认 confirm

例如:

{
  type: 'confirm',
  name: 'toBeDelivered',
  message: '您的心情怎么样?(Y:好,N:不好)',
  default: true,
},

(6)输入 input

例如:

以下是引导脚手架用户输入手机号码,并在手机号码输入错误的时候提示“请输入合法的手机号码!”例子:

{
  type: 'input',
  name: 'phone',
  message: "您的手机号码是?",
  validate(value) {
    const pass = value.match(
      /^([01]{1})?[-.\s]?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})\s?((?:#|ext\.?\s?|x\.?\s?){1}(?:\d+)?)?$/i
    );
    if (pass) {
      return true;
    }
    return '请输入合法的手机号码!';
  },
},

以下是另外一个完整的示例:

const inquirer = require('inquirer');
inquirer
  .prompt([
    {
      type: 'list',
      name: 'drinks',
      message: '😘你想来点什么呢?',
      choices: [
        '咖啡',
        '奶茶'
      ],
    },
    {
      type: 'list',
      name: 'size',
      message: '😘我们有以下几种被型,您要哪种呢?',
      choices: ['小杯(Small)', '中杯(Middle)', '大杯(Large)', '特大杯(Super Large)'],
      filter(val) {
        return val.toLowerCase();
      },
    },
  ])
  .then((answers) => {
    console.log(`您要的是${answers.size}的${answers.drinks}!`);
  });

其运行效果如下:

(7)数字 number

例如:


         

(8)密码 password

例如:

const inquirer = require('inquirer');
const requireLetterAndNumber = (value) => {
  if (/\w/.test(value) && /\d/.test(value)) {
    return true;
  }
  return '错误:密码至少需要有一个字母和一个数字';
};
inquirer
  .prompt([
    {
      type: 'password',
      message: '这是不显示任何字符的密码输入形式 - 请输入密码:',
      name: 'psw1',
      validate: requireLetterAndNumber,
    },
    {
      type: 'password',
      message: '这是使用屏蔽符显示密码输入形式 - 请输入密码:',
      name: 'psw2',
      mask: '*',
      validate: requireLetterAndNumber,
    },
  ])
  .then((answers) => {
    console.log(`完全不显示输入的密码是:${answers.psw1},显示为屏蔽符输入的密码是:${answers.psw2}`);
  });

其效果如下:

(9)编辑器 editor

例如:


         

5. 终端中使用色彩模块 kolorist

不推荐使用。

5.1 安装 kolorist

npm install kolorist
# 或者
yarn add kolorist
# 或者
pnpm install kolorist

5.2 kolorist 用法

5.2.1 基础示例

import { red, cyan } from 'kolorist';
const line = red(`Error: something failed in ${cyan('my-file.js')}.`;
console.log(line));

不仅是 console.log ,使用其它控制台输出,甚至是打印抛出错误的文本,都可以 通过 kolorist 来控制颜色,例如:

import { red } from 'kolorist'
// ...
if (values.shouldOverwrite === false) {
  throw new Error(red('✖') + ' Operation cancelled')
}

5.2.2 接口

该模块接口声明如下:

export declare const enum SupportLevel {
    none = 0,
    ansi = 1,
    ansi256 = 2
}
export declare let options: {
    enabled: boolean;
    supportLevel: SupportLevel;
};
export declare function stripColors(str: string | number): string;
export declare const reset: (str: string | number) => string;
export declare const bold: (str: string | number) => string;
export declare const dim: (str: string | number) => string;
export declare const italic: (str: string | number) => string;
export declare const underline: (str: string | number) => string;
export declare const inverse: (str: string | number) => string;
export declare const hidden: (str: string | number) => string;
export declare const strikethrough: (str: string | number) => string;
export declare const black: (str: string | number) => string;
export declare const red: (str: string | number) => string;
export declare const green: (str: string | number) => string;
export declare const yellow: (str: string | number) => string;
export declare const blue: (str: string | number) => string;
export declare const magenta: (str: string | number) => string;
export declare const cyan: (str: string | number) => string;
export declare const white: (str: string | number) => string;
export declare const gray: (str: string | number) => string;
export declare const lightGray: (str: string | number) => string;
export declare const lightRed: (str: string | number) => string;
export declare const lightGreen: (str: string | number) => string;
export declare const lightYellow: (str: string | number) => string;
export declare const lightBlue: (str: string | number) => string;
export declare const lightMagenta: (str: string | number) => string;
export declare const lightCyan: (str: string | number) => string;
export declare const bgBlack: (str: string | number) => string;
export declare const bgRed: (str: string | number) => string;
export declare const bgGreen: (str: string | number) => string;
export declare const bgYellow: (str: string | number) => string;
export declare const bgBlue: (str: string | number) => string;
export declare const bgMagenta: (str: string | number) => string;
export declare const bgCyan: (str: string | number) => string;
export declare const bgWhite: (str: string | number) => string;
export declare const bgGray: (str: string | number) => string;
export declare const bgLightRed: (str: string | number) => string;
export declare const bgLightGreen: (str: string | number) => string;
export declare const bgLightYellow: (str: string | number) => string;
export declare const bgLightBlue: (str: string | number) => string;
export declare const bgLightMagenta: (str: string | number) => string;
export declare const bgLightCyan: (str: string | number) => string;
export declare const bgLightGray: (str: string | number) => string;
export declare const ansi256: (n: number) => (str: string | number) => string;
export declare const ansi256Bg: (n: number) => (str: string | number) => string;
export declare function link(text: string, url: string): string;

6. 终端字符串样式模块: chalk

另一个功能上比较相似的模块是 chalk (粉笔)。

6666666666666.png


该模块可以采用功能更强大的颜色计算与终端输出工具 jc-color 替代,请参考博客《NodeJS、Borwser 中制作漂亮的炫彩控制台》


777777777777777.gif

6.1 chalk 的安装

npm install chalk
# 或者
yarn add chalk
# 或者
pnpm install chalk

6.2 chalk 的用法

6.2.1 入门案例

import chalk from 'chalk';
const lines = [
  chalk.blue('Hello') + ' World' + chalk.red('!'),  // 组合样式字符串和普通字符串
  chalk.blue.bgRed.bold('Hello world!'),           // 使用可链接API合成多种样式
  chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz'),    // 传入多个参数
  chalk.red('Hello', chalk.underline.bgBlue('world') + '!'),    // 嵌套样式
  chalk.green(
    '我是绿行 ' +
    chalk.blue.underline.bold('带有蓝色子串') +
    ' 那又变成绿色了!'
  ),
  // 使用模板字符串
  `
CPU: ${chalk.red('90%')}
RAM: ${chalk.green('40%')}
DISK: ${chalk.yellow('70%')}
`,
  chalk.rgb(123, 45, 67).underline('带下划线的红色'), // 在支持RGB颜色的终端模拟器中使用它。
  chalk.hex('#DEADED').bold('Bold gray!')            
];
for (const line of lines) {
  console.log(line);
}

其效果如下:

6666666666666.png

6.2.2 ChalkInstance 接口

interface ChalkInstance {
  (...text: unknown[]): string;
  /**
  Chalk 的颜色支持。
  默认情况下,会根据环境自动检测颜色支持。
  Levels:
  - `0` - 禁用所有颜色。
  - `1` - 基本16色支持。
  - `2` - ANSI 256颜色支持。
  - `3` - Truecolor支持1600万色。
  */
  level: ColorSupportLevel;
  rgb: (red: number, green: number, blue: number) => this;    // 使用RGB值设置文本颜色。
  hex: (color: string) => this;          // 使用十六进制值设置文本颜色。
  ansi256: (index: number) => this;      // 使用一个8位无符号数设置文本颜色。
  bgRgb: (red: number, green: number, blue: number) => this;  // 使用RGB值设置背景颜色。
  bgHex: (color: string) => this;        // 使用十六进制值设置背景颜色。
  bgAnsi256: (index: number) => this;    // 使用一个8位无符号数设置背景色。
  readonly reset: this;         // Modifier: 重置当前样式。
  readonly bold: this;          // Modifier: 将文本加粗。
  readonly dim: this;           // Modifier: 使文本具有较低的不透明度。
  readonly italic: this;        // Modifier: 将文本设为斜体。 *(未得到广泛支持)*
  readonly underline: this;     // Modifier: 在文本下面放一条水平线。 *(未得到广泛支持)*
  readonly overline: this;      // Modifier: 在文本上方画一条水平线。 *(未得到广泛支持)*
  readonly inverse: this;       // Modifier: 反转背景和前景颜色。
  readonly hidden: this;        // Modifier: 打印文本,但使其不可见。
  readonly strikethrough: this;  // Modifier: 在文本中心画一条水平线。 * (未得到广泛支持)*
  // Modifier: 仅当 Chalk 的色阶高于零时才打印文本。
  // 对纯装饰性的东西很有用。
  readonly visible: this;
  readonly black: this;
  readonly red: this;
  readonly green: this;
  readonly yellow: this;
  readonly blue: this;
  readonly magenta: this;
  readonly cyan: this;
  readonly white: this;
  readonly gray: this;           // `blackBright` 的别名。
  readonly grey: this;           // `blackBright`的别名。
  readonly blackBright: this;
  readonly redBright: this;
  readonly greenBright: this;
  readonly yellowBright: this;
  readonly blueBright: this;
  readonly magentaBright: this;
  readonly cyanBright: this;
  readonly whiteBright: this;
  readonly bgBlack: this;
  readonly bgRed: this;
  readonly bgGreen: this;
  readonly bgYellow: this;
  readonly bgBlue: this;
  readonly bgMagenta: this;
  readonly bgCyan: this;
  readonly bgWhite: this;
  readonly bgGray: this;         // `bgBlackBright`的别名。
  readonly bgGrey: this;         // `bgBlackBright`的别名。
  readonly bgBlackBright: this;
  readonly bgRedBright: this;
  readonly bgGreenBright: this;
  readonly bgYellowBright: this;
  readonly bgBlueBright: this;
  readonly bgMagentaBright: this;
  readonly bgCyanBright: this;
  readonly bgWhiteBright: this;
}

7. 同时用于 NodeJS 和 浏览器的控制台记录器:consola

7.1 consola 介绍与安装

7.2 consola 的用法

7.2.1 基本用法示范

const consola = require('consola')
// 也可以使用 ejs,注意不同环境:
// import consola from 'consola/src/node'
// import consola from 'consola/src/browser'
// See types section for all available types
consola.success('Built!')
consola.info('Reporter: Some info')
consola.error(new Error('Foo'))

8. 简单的CLI提示查询用户信息:prompts

8.1 安装 prompts

npm install prompts
# 或
yarn add prompts
# 或
pnpm install prompts

8.2 用法解析

8.2.1 入门示例

import prompts from 'prompts';
const questions = [
  {
    type: 'select',
    name: 'meaning',
    message: 'What is your meaning of life?',
    choices: [
      { title: 'Handsome Boy', value: 'Handsome Boy' },
      { title: 'Beautiful Woman', value: 'Beautiful Woman' }
    ],
  },
  {
    type: 'select',
    name: 'gender',
    message: 'Are you a boy or a girl?',
    choices: [
      { title: 'Girl', value: 'firl' },
      { title: 'Boy', value: 'boy' }
    ],
  },
  {
    type: 'number',
    name: 'age',
    message: 'How old are you?'
  }
];
(async () => {
  const response = await prompts(questions);
  console.log('------------------------------------------');
  console.log(`>> Your are a ${response.gender} loves ${response.meaning} and whose age is ${response.age}.`);
  console.log('------------------------------------------');
})();

8.2.2 提示类型

类型名 语法格式 描述
text text(message, [initial], [style]) 自由文本输入的文本提示。
password password(message, [initial]) 自由文本输入的文本提示。
invisible invisible(message, [initial]) 提示用户输入不可见的文本。
number number(message, initial, [max], [min], [style]) 提示用户输入不可见的文本。
confirm confirm(message, [initial]) 经典是/否提示。
list list(message, [initial]) 返回数组的列表提示。
toggle toggle(message, [initial], [active], [inactive]) 返回数组的列表提示。
select select(message, choices, [initial], [hint], [warn]) 返回数组的列表提示。
multiselect multiselect(message, choices, [initial], [max], [hint], [warn]) 交互式多选提示。
autocompleteMultiselect autocompleteMultiselect(same) 交互式多选提示。 自动完成是具有相同选项的可搜索多选提示。对于长列表很有用。
autocomplete autocomplete(message, choices, [initial], [suggest], [limit], [style]) 交互式自动完成提示。
date date(message, [initial], [warn]) 交互式自动完成提示。

9. 终端旋转亮片: ora

9.1 ora 的安装

npm install ora
# or
yarn add ora
# or
pnpm install ora

9.2 快速入门

import ora from 'ora';
const spinner = ora('Loading').start();
setTimeout(() => {
  spinner.color = 'yellow';
  spinner.text = 'Loading ...';
}, 1000);

777777777777777.gif


9.3 Ora 接口

我们使用的 ora 函数的类型声明如下:

export default function ora(options?: string | Options): Ora;

如果 ora 函数提供了 options 字符串,它将被视为 options.text 的快捷方式。其中参数 options 除了可以是一个字符串外还可以是一个

export interface Options {
  // 旋转亮片后显示的文本。
  readonly text?: string;
  // 文本或返回文本以在旋转亮片前显示的函数。如果设置为空字符串,将不显示前缀文本。
  readonly prefixText?: string | PrefixTextGenerator;
  //  所提供的旋转亮片之一的名称。在Windows上,它将总是使用line旋转亮片,因为Windows命令行没有适当的Unicode支持。
  readonly spinner?: SpinnerName | Spinner;
  // 旋转亮片的颜色。
    // 默认值为 'cyan'
  readonly color?: Color;
  // 设置为'false' 以阻止Ora 隐藏光标。
  // 默认值为 true
  readonly hideCursor?: boolean;
  // 给定的空格数缩进旋转亮片。
  // 默认值为 0
  readonly indent?: number;
  // 每帧(frame)之间的间隔。
  // 旋转亮片提供了它们自己推荐的时间间隔,所以您实际上不需要指定。
  // 默认值: 由旋转器或“100”提供。
  readonly interval?: number;
  // 来写入输出的流。
    // 例如,您可以将其设置为“process.stdout”。
  // 默认值为 process.stderr
  readonly stream?: NodeJS.WritableStream;
  /**
  Force enable/disable the spinner. If not specified, the spinner will be enabled if the `stream` is being run inside a TTY context (not spawned or piped) and/or not in a CI environment.
  Note that `{isEnabled: false}` doesn't mean it won't output anything. It just means it won't output the spinner, colors, and other ansi escape codes. It will still log text.
  */
  readonly isEnabled?: boolean;
  // 禁用旋转亮片和所有日志文本。所有输出都被抑制,并且“isEnabled”将被视为“false”。
  // 默认值为 false
  readonly isSilent?: boolean;
  // 如果是TTY,在运行时丢弃标准输入(除了Ctrl+C)。这防止了旋转器在输入时抖动,在按下“Enter”键时输出虚线,并且防止了旋转器运行时输入的缓冲。
    // 这对Windows没有影响,因为没有好的方法来正确地实现丢弃stdin。
  // 默认值为 true
  readonly discardStdin?: boolean;
}

ora 函数返回一个 Ora 接口的对象,Ora 类型的接口声明如下:

export interface Ora {
  // 更改旋转亮片后的文本。
  text: string;
  // 更改在旋转亮片之前返回文本的文本或函数。
    // 如果设置为空字符串,将不显示前缀文本。
  prefixText: string;
  // 改旋转亮片颜色。
  color: Color;
  // 更改旋转亮片缩进。
  indent: number;
  // 获取旋转亮片
  get spinner(): Spinner;
  // 设置旋转亮片
  set spinner(spinner: SpinnerName | Spinner);
  // 实例当前是否正在旋转的布尔值。
  get isSpinning(): boolean;
  // 每帧之间的间隔。
  // 间隔由选定的旋转亮片决定。
  get interval(): number;
  // 启动旋转亮片
  // text - 设置的当前文本
  // 返回旋转亮片实例
  start(text?: string): this;
  // 停止和清除旋转亮片
  // 返回旋转亮片实例
  stop(): this;
  // 停止旋转亮片,将其更改为绿色`✔`,并保持当前文本,或'text'(如果提供)。
    //  text - 如果提供,将保留文本。
  // 返回旋转亮片实例
  succeed(text?: string): this;
  // 停止旋转,将其更改为红色`✖`,并保留当前文本,或“text”(如果提供)。
    // text - 如果提供,将保留文本。
  // 返回旋转亮片实例
  fail(text?: string): this;
  // 停止旋转亮片,将其更改为黄色`⚠`,并保持当前文本,或'text' (如果提供)。
  //  text - 如果提供,将保留文本。
  // 返回旋转亮片实例
  warn(text?: string): this;
  // 停止旋转亮片,将其更改为蓝色`ℹ`,并保持当前文本,或'文本'(如果提供)。
  //  text - 如果提供,将保留文本。
  // 返回旋转亮片实例
  info(text?: string): this;
  // 停止旋转亮片并更改符号或文本。
  // 返回旋转亮片实例
  stopAndPersist(options?: PersistOptions): this;
  // 清除旋转亮片
  // 返回旋转亮片实例
  clear(): this;
  // 手动渲染一个新的 frame.
  // 返回旋转亮片实例
  render(): this;
  // 获取一个新的 frame.
    // 旋转亮片实例文本。
  frame(): string;
}

10. 终端渐变色字符串:gradient-string

10.1 简介与安装

还记得你使用 create-vue 脚手架 创建 vue 项目时出现的炫酷文字吗:

6666666666666.png

gradient-string 模块就可以实现这样的效果。其安装方法为:

npm i gradient-string
# 或
yarn add gradient-string
# 或
pnpm i gradient-string

该模块可以采用功能更强大的颜色计算与终端输出工具 jc-color 替代,请参考博客《NodeJS、Borwser 中制作漂亮的炫彩控制台》

777777777777777.gif

10.2 gradient-string 入门案例

import gradient from 'gradient-string';
const g = gradient('cyan', 'pink')('Hello world!');
console.log(g);

6666666666666.png

11. 终端彩色文字动画: chalk-animation

11.1 简介与安装

如果终端彩色渐变字还不能让你感觉过瘾,那么就的好好试试这款炫酷的彩色文字动画模块了,它就是 chalk-animation,其安装方式如下:

npm i chalk-animation
# 或
yarn add chalk-animation
# 或
pnpm i chalk-animation

11.2 chalk-animation 入门案例

13. 其它工具

8.1 通过boxen 在终端中创建方框

5.2.1 安装 kolorist

npm install boxen
# 或者
yarn add boxen
# 或者
pnpm install boxen
目录
相关文章
|
1月前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js:从零开始构建后端服务
【10月更文挑战第42天】在数字时代的浪潮中,掌握一门后端技术对于开发者来说至关重要。Node.js,作为一种基于Chrome V8引擎的JavaScript运行环境,允许开发者使用JavaScript编写服务器端代码,极大地拓宽了前端开发者的技能边界。本文将从Node.js的基础概念讲起,逐步引导读者理解其事件驱动、非阻塞I/O模型的核心原理,并指导如何在实战中应用这些知识构建高效、可扩展的后端服务。通过深入浅出的方式,我们将一起探索Node.js的魅力和潜力,解锁更多可能。
|
29天前
|
JSON 缓存 JavaScript
深入浅出:使用Node.js构建RESTful API
在这个数字时代,API已成为软件开发的基石之一。本文旨在引导初学者通过Node.js和Express框架快速搭建一个功能完备的RESTful API。我们将从零开始,逐步深入,不仅涉及代码编写,还包括设计原则、最佳实践及调试技巧。无论你是初探后端开发,还是希望扩展你的技术栈,这篇文章都将是你的理想指南。
|
22天前
|
JSON JavaScript 前端开发
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将引导您步入Node.js的奇妙世界,通过实践操作,掌握如何使用这一强大的JavaScript运行时环境构建高效、可扩展的RESTful API。我们将一同探索Express框架的使用,学习如何设计API端点,处理数据请求,并实现身份验证机制,最终部署我们的成果到云服务器上。无论您是初学者还是有一定基础的开发者,这篇文章都将为您打开一扇通往后端开发深层知识的大门。
38 12
|
29天前
|
JavaScript NoSQL API
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发如同一座灯塔,指引着数据的海洋。本文将带你航行在Node.js的海域,探索如何从一张白纸到完成一个功能完备的RESTful API。我们将一起学习如何搭建开发环境、设计API结构、处理数据请求与响应,以及实现数据库交互。准备好了吗?启航吧!
|
1月前
|
缓存 负载均衡 JavaScript
构建高效后端服务:Node.js与Express框架实践
在数字化时代的浪潮中,后端服务的重要性不言而喻。本文将通过深入浅出的方式介绍如何利用Node.js及其强大的Express框架来搭建一个高效的后端服务。我们将从零开始,逐步深入,不仅涉及基础的代码编写,更会探讨如何优化性能和处理高并发场景。无论你是后端新手还是希望提高现有技能的开发者,这篇文章都将为你提供宝贵的知识和启示。
|
1月前
|
JSON JavaScript API
深入浅出Node.js:从零开始构建RESTful API
【10月更文挑战第39天】 在数字化时代的浪潮中,API(应用程序编程接口)已成为连接不同软件应用的桥梁。本文将带领读者从零基础出发,逐步深入Node.js的世界,最终实现一个功能完备的RESTful API。通过实践,我们将探索如何利用Node.js的异步特性和强大的生态系统来构建高效、可扩展的服务。准备好迎接代码和概念的碰撞,一起解锁后端开发的新篇章。
|
1月前
|
JSON JavaScript 前端开发
使用JavaScript和Node.js构建简单的RESTful API
使用JavaScript和Node.js构建简单的RESTful API
|
1月前
|
Web App开发 JavaScript 前端开发
2024年5月node.js安装(winmac系统)保姆级教程
本篇博客为2024年5月版Node.js安装教程,适用于Windows和Mac系统。作者是一名熟悉JavaScript与Vue的大一学生,分享了Node.js的基本介绍、下载链接及简单安装步骤。安装完成后,通过终端命令`node -v`验证版本即可确认安装成功。欢迎关注作者,获取更多技术文章。
32 2
2024年5月node.js安装(winmac系统)保姆级教程
|
1月前
|
存储 JavaScript 搜索推荐
Node框架的安装和配置方法
安装 Node 框架是进行 Node 开发的第一步,通过正确的安装和配置,可以为后续的开发工作提供良好的基础。在安装过程中,需要仔细阅读相关文档和提示,遇到问题及时解决,以确保安装顺利完成。
92 2
|
3月前
|
JavaScript
NodeJs的安装
文章介绍了Node.js的安装步骤和如何创建第一个Node.js应用。包括从官网下载安装包、安装过程、验证安装是否成功,以及使用Node.js监听端口构建简单服务器的示例代码。
NodeJs的安装