前端架构师-week5-命令行交互原理

news2025/1/31 3:05:00

目录

加餐:命令行交互原理

学习路径

readline 源码分析

如何开发命令行交互列表

实现原理

 架构图

本章学习路径和学习目标

readline 的使用方法和实现原理

高能:深入讲解 readline 键盘输入监听实现原理

秀操作:手写 readline 核心实现

命令行样式修改的核心原理:ansi 转移序列讲解

 响应式库 rxjs 快速入门

放大招:手写命令行交互式列表组件(上)(下)

inquirer 源码执行流程分析


加餐:命令行交互原理

学习路径

·掌握:readline / events / stream / ansi-escapes(实现命令行中特殊显示) / rxjs(响应式模型库)

·掌握命令行交互的实现原理,并实现一个可交互的列表

·分析 inquirer 源码掌握其中的关键实现

ANSI-escape-code 查阅文档:https://handwiki.org/wiki/ANSI_escape_code

readline 源码分析

·强制将函数转换为构建函数

if (!(this instance Interface)) {
    return new Interface(input, output, completer, terminal)
}

·继承 EventEmitter

EventEmitter.call(this)

·监听键盘事件 

emitKeypressEvents(input, this)

// `input` usually refers to stdin
input.on('keypress', onkeypress)
input.on('end', ontermend)

readline 核心实现原理: 

 

注:readline 利用了 Generator 函数的特性,还不熟悉 Generator 函数的同学可以查看 https://es6.ruanyifeng.com/#docs/generator 

如何开发命令行交互列表

实现原理

 架构图

 

 

  

本章学习路径和学习目标

  ......

readline 的使用方法和实现原理

        nodejs 内置库,主要帮助我们管理输入流的。命令当中要交互的方式一定是需要用户提供一些输入的。readline 就可以很好的帮我们去一次一次地读取输入流。这里要注意的是,这个输入不只是我们输入一些字符,还包括输入我们键盘上的内容,如 上下左右 esc 回车 空格,这些都在输入流的监听范围内。

// Usage:

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

rl.question('your name: ', (answer => {
  console.log(answer);

  rl.close();  // 需要手动关闭
}));

高能:深入讲解 readline 键盘输入监听实现原理

// generator 基础知识

function* g() {
  console.log('read');
  let ch = yield;
  console.log(ch);
  let s = yield;
  console.log(s);
}

const f = g();
f.next();
f.next('a');
f.next('b');
// 输出 read a b

秀操作:手写 readline 核心实现

function stepRead(callback) {
  // readline 源码为什么那么长,在处理各种用户输入的场景,如 回车 ctrl+C。场景是特别复杂的。
  // 每输入一次就会监听。所有模式全部由开发者控制。原生模式,所有内容都不会帮你实现了。
  function onkeypress(s) {
    output.write(s);  // 命令行打印。
    line += s;
    switch (s) {
      case '\r':
        input.pause();
        callback(line);
        break;
    }
  }

  const input = process.stdin;
  const output = process.stdout;
  let line = '';

  emitKeypressEvents(input);
  input.on('keypress', onkeypress);

  input.setRawMode(true);
  input.resume();
}

function emitKeypressEvents(stream) {
  function onData(chunk) {
    g.next(chunk.toString());  // toString 否则是个buffer。
  }

  const g = emitKeys(stream);
  g.next();

  stream.on('data', onData);
}

function* emitKeys(stream) {
  while (true) {
    let ch = yield;
    stream.emit('keypress', ch);
  }
}

stepRead(function(s) {
  console.log('answer:' + s);
});

命令行样式修改的核心原理:ansi 转移序列讲解

        简单来说,定义一个规范,这个规范可以让我们在终端当中通过转义字符实现一些特殊操作。如:将光标上移或者下移 换行等,输入信息擦除,字体加粗倾斜变色等。

console.log('\x1B[41m\x1B[4m%s\x1B[0m', 'your name:');
// \x1B[4m 文字加下划线
// \x 十六进制,1B 是固定的,%s 占位符。
// 41m 红色,0m 无色。
// m 表示渲染的参数
// B 表示光标下移
// G 表示水平移动
console.log('\x1B[2B%s', 'your name2:')
console.log('\x1B[2G%s', 'your name3:')

        概念知与不知道,有天壤之别。 

 响应式库 rxjs 快速入门

        异步库,和 promise 相似。inquirer 源码当中,大量使用 rxjs 去做回调、事件绑定监听。

const { range } = require('rxjs');
const { map, filter } = require('rxjs/operators');

const pipe = range(1, 200).pipe(
  filter(x => x % 2 === 1),
  map(x => x + x),
  filter(x => x % 3 === 0),
  filter(x => x % 6 === 0),
  filter(x => x % 9 === 0),
);

pipe.subscribe(x => console.log(x));

// 会打印符合条件的数字

放大招:手写命令行交互式列表组件(上)(下)

npm install -S mute-stream
npm install -S rxjs
npm install -S ansi-escapes  # 清屏
const EventEmitter = require('events');
const readline = require('readline');
const MuteStream = require('mute-stream');
const { fromEvent } = require('rxjs');
const ansiEscapes = require('ansi-escapes');

const option = {
  type: 'list',
  name: 'name',
  message: 'select your name:',
  choices: [{
    name: 'sam', value: 'sam',
  }, {
    name: 'shuangyue', value: 'sy',
  }, {
    name: 'zhangxuan', value: 'zx',
  }],
};

function Prompt(option) {
  return new Promise((resolve, reject) => {
    try {
      const list = new List(option);
      list.render();
      list.on('exit', function(answers) {
        resolve(answers);
      })
    } catch (e) {
      reject(e);
    }
  });
}

class List extends EventEmitter {  // 完成事件的监听
  constructor(option) {
    super();
    this.name = option.name;
    this.message = option.message;
    this.choices = option.choices;
    this.input = process.stdin;
    const ms = new MuteStream();
    ms.pipe(process.stdout);  // 对 output 封装 
    this.output = ms;
    this.rl = readline.createInterface({
      input: this.input,
      output: this.output,
    });
    this.selected = 0;
    this.height = 0;
    this.keypress = fromEvent(this.rl.input, 'keypress')
      .forEach(this.onkeypress);
    this.haveSelected = false; // 是否已经选择完毕
  }

  onkeypress = (keymap) => {
    const key = keymap[1];
    if (key.name === 'down') {
      this.selected++;
      if (this.selected > this.choices.length - 1) {
        this.selected = 0;
      }
      this.render();
    } else if (key.name === 'up') {
      this.selected--;
      if (this.selected < 0) {
        this.selected = this.choices.length - 1;
      }
      this.render();
    } else if (key.name === 'return') {
      this.haveSelected = true;
      this.render();
      this.close();
      this.emit('exit', this.choices[this.selected]);
    }
  };

  render() {
    this.output.unmute();  // 解封输出
    this.clean();
    this.output.write(this.getContent());
    this.output.mute();  // 禁止输出用户无法再输入东西
  }

  getContent = () => {
    if (!this.haveSelected) {
      let title = '\x1B[32m?\x1B[39m \x1B[1m' + this.message + '\x1B[22m\x1B[0m \x1B[0m\x1B[2m(Use arrow keys)\x1B[22m\n';
      this.choices.forEach((choice, index) => {
        if (index === this.selected) {
          // 判断是否为最后一个元素,如果是,则不加\n
          if (index === this.choices.length - 1) {
            title += '\x1B[36m❯ ' + choice.name + '\x1B[39m ';
          } else {
            title += '\x1B[36m❯ ' + choice.name + '\x1B[39m \n';
          }
        } else {
          if (index === this.choices.length - 1) {
            title += '  ' + choice.name;
          } else {
            title += '  ' + choice.name + '\n';
          }
        }
      });
      this.height = this.choices.length + 1;
      return title;
    } else {
      // 输入结束后的逻辑
      const name = this.choices[this.selected].name;
      let title = '\x1B[32m?\x1B[39m \x1B[1m' + this.message + '\x1B[22m\x1B[0m \x1B[36m' + name + '\x1B[39m\x1B[0m \n';
      return title;
    }
  };

  clean() {
    const emptyLines = ansiEscapes.eraseLines(this.height);
    this.output.write(emptyLines);
  }

  close() {
    this.output.unmute();
    this.rl.output.end();
    this.rl.pause();
    this.rl.close();
  }
}

Prompt(option).then(answers => {
  console.log('answers:', answers);
});

inquirer 源码执行流程分析

......

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/521252.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL---控制流函数、窗口函数(序号函数、开窗聚合函数、分布函数、前后函数、头尾函数、其他函数)

1. 控制流函数 格式 解释 案例 IF(expr,v1,v2) 如果表达式 expr 成立&#xff0c;返回结果 v1&#xff1b;否则&#xff0c;返回结果 v2。 SELECT IF(1 > 0,正确,错误) ->正确 IFNULL(v1,v2&#xff09; 如果 v1 的值不为 NULL&#xff0c;则返回 v1&#xff…

JVM垃圾收集器(一)

目录 1、如何考虑 GC 2、如何确定一个对象“死去” 3、分代收集理论 4、垃圾回收算法 5、HotSpot的算法实现细节 1、如何考虑 GC 垃圾收集&#xff08;Garbage Collection&#xff0c;GC&#xff09;的历史比Java更久远&#xff0c;1960年诞生于MIT。 GC 需要考虑的三件事…

UNIAPP实战项目笔记72 提交订单到选择支付方式的前后端交互

UNIAPP实战项目笔记72 提交订单到选择支付方式的前后端交互 思路 购物车确认订单,跳转到订单确认界面确认支付后清除购物车对应id的数据 实例截图 清空购物车数据后 代码 前端代码 order.js export default{state:{// 订单号orderNumber:},getters:{},mutations:{initOr…

数字化转型浪潮下,如何选择适合企业的低代码平台

近日&#xff0c;艾瑞咨询发布了《数字新生态&#xff1a;中国低代码厂商发展白皮书》&#xff08;以下简称“报告”&#xff09;&#xff0c;在该报告中&#xff0c;艾瑞咨询对中国当前的低代码市场进行了非常细致的解构&#xff0c;并针对当前企业数字化转型&#xff0c;对低…

电阻传感器工作原理

金属随着温度变化&#xff0c;其电阻值也发生变化。 对于不同金属来说&#xff0c;温度每变化一度&#xff0c;电阻值变化是不同的&#xff0c;而电阻值又可以直接作为输出信号。 电阻共有两种变化类型 正温度系数 温度升高 阻值增加 温度降低 阻值减少 负温度系数 温…

【一起啃书】《机器学习》第七章 贝叶斯分类器

文章目录 第七章 贝叶斯分类器7.1 贝叶斯决策论7.2 极大似然估计7.3 朴素贝叶斯分类器7.4 半朴素贝叶斯分类器7.5 贝叶斯网7.6 EM算法 第七章 贝叶斯分类器 7.1 贝叶斯决策论 对分类任务来说&#xff0c;在所有相关概率都已知的理想情形下&#xff0c;贝叶斯决策论考虑如何基于…

k8s学习-CKS真题-利用AppArmor进行应用行为限制

目录 题目环境搭建解题模拟题参考 题目 Task 在 cluster 的工作节点 node02 上&#xff0c;实施位于 /etc/apparmor.d/nginx_apparmor 的现有 APPArmor 配置文件。 编辑位于 /cks/KSSH00401/nginx-deploy.yaml 的现有清单文件以应用 AppArmor 配置文件。 最后&#xff0c;应用清…

UE4及Airsim安装时遇到的问题及解决办法

UE4及Airsim安装时遇到的问题及解决办法 目录 UE4及Airsim安装时遇到的问题及解决办法前言UE4下载慢解决方法 Airsim编译过程中提示&#xff1a;无法打开包括文件: “Eigen/Dense”: No such file or directory [D:\software\Visual_studio2022\2022Community\AirSim\Air解决办…

C语言——运算符和表达式

所谓表达式就是指由运算符、运算量和标点符号组成的有效序列&#xff0c;其目的是说明一个计算过程。表达式可以独立成语句&#xff1a;表达式; 运算符按功能分为&#xff1a;算术运算、赋值运算、关系运算、逻辑运算、位运算以及其他运算符 1. 算术运算符&#xff1a; - * / %…

项目部署 | Linux安装Git和Maven

知识目录 一、写在前面✨二、安装Git&#x1f495;2.1 yum安装git2.2 新建Git仓库2.3 拉取仓库代码 三、安装Maven&#x1f495;3.1 上传Maven压缩包并解压3.2 配置环境变量3.3 设置本地仓库3.4 设置中央仓库 四、总结撒花&#x1f60a; 一、写在前面✨ 大家好&#xff01;我是…

二叉树的递归遍历与迭代遍历(图示)

文章目录 前言1. 二叉树的递归遍历&#xff08;一入递归深似海&#xff0c;从此offer是路人&#xff09;1.1 [前序遍历](https://leetcode.cn/problems/binary-tree-preorder-traversal/)1.2 [中序遍历](https://leetcode.cn/problems/binary-tree-inorder-traversal/)1.3 [后序…

实验一 Python基础编程

实验一 Python基础编程 只为给原因学习编程的同学提供一个思路&#xff0c;让编程更简单&#xff01;&#xff01;&#xff01; 本博主擅长整理粉丝的私信&#xff01;只要你有需求就可以告诉博主&#xff01;博主可以帮你解决并发表&#xff01; 一、实验学时 2学时 二、实…

docker发布到dockerhub报错denied: requested access to the resource is denied

docker发布到dockerhub报错denied: requested access to the resource is denied 解决方案 修改发布的镜像的REPOSITORY为自己的账户名镜像&#xff0c;比如我的用户名是luobotoutou123。docker tag tomcat02:1.0 luobotoutou123/tomcat02:1 然后发布镜像 到dockerhub远程仓库…

学习杂记 2023.5.13 单词背诵

目录 鼠标上的DPI是什么&#xff1f; 鼠标上的DPI是什么&#xff1f; DPI是英文Dots Per Inch的缩写&#xff0c;意思是每英寸点数。在计算机中&#xff0c;DPI通常用于描述指针设备&#xff08;例如鼠标&#xff09;的精度。在鼠标上&#xff0c;DPI指的是鼠标移动时指针在屏…

[图神经网络]ViG(Vision GNN)网络代码实现

论文解读&#xff1a; [图神经网络]视觉图神经网络ViG(Vision GNN)--论文阅读https://blog.csdn.net/weixin_37878740/article/details/130124772?spm1001.2014.3001.5501代码地址&#xff1a; ViGhttps://github.com/huawei-noah/Efficient-AI-Backbones/tree/master/vig_p…

Hive之DDL

目录 对数据库操作&#xff1a; 创建数据库&#xff1a; 查看数据库信息&#xff1a; 1.查看基本信息&#xff1a; 2.查看详尽信息&#xff1a; 删除数据库&#xff1a; 1.简单语法&#xff1a; 2.复杂语法&#xff1a; 对表操作&#xff1a; 创建表&#xff1a; 1.普…

JVM-内存结构

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;JVM &#x1f96d;本文内容&…

《程序员的底层思维》读书笔记

人是能够习惯于任何环境的生物&#xff0c;之前你认为自己难以克服的困难&#xff0c;慢慢都会适应了。 维克多弗兰克《活出生命的意义》 文章目录 人是能够习惯于任何环境的生物&#xff0c;之前你认为自己难以克服的困难&#xff0c;慢慢都会适应了。 基础思维能力逻辑思维批…

每日学术速递5.12

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.ImageBind: Holistic AI learning across six modalities 标题&#xff1a;ImageBind&#xff1a;跨六种模式的整体人工智能学习 作者&#xff1a;Mengyuan Yan Jessica Lin Mont…

支付系统设计三:渠道网关设计04-渠道数据补全

文章目录 前言一、交易信息准备1. MessageDescription内容2. 交易信息填充3. 开户机构信息填充4. 省市区域信息填充5. 银行信息填充 二、路由处理三、支付渠道数据补全1.服务端支付渠道获取2. 支付渠道通用数据补全2.1 支付渠道账户信息补全2.1 商户信息补全结束 3. 支付渠道差…