最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺析node命令行交互原理

 更新時(shí)間:2023年05月24日 09:31:13   作者:PHM  
當(dāng)我們使用腳手架去創(chuàng)建一個(gè)項(xiàng)目的時(shí)候,通常會(huì)通過(guò)命令行交互來(lái)獲取一些信息,比如填項(xiàng)目名稱(chēng),選擇項(xiàng)目模板,選擇版本,我們雖然經(jīng)常用到,但是想必對(duì)于其中的原理還是不太了解,本文將待大家詳細(xì)介紹一下node命令行的交互原理,需要的朋友可以參考下

什么是命令行交互

當(dāng)我們使用腳手架去創(chuàng)建一個(gè)項(xiàng)目的時(shí)候,通常會(huì)通過(guò)命令行交互來(lái)獲取一些信息:比如填項(xiàng)目名稱(chēng);選擇項(xiàng)目模板;選擇版本;需要安裝哪些額外的工具等等。這些我們雖然經(jīng)常用到,但是想必對(duì)于其中的原理還是不太了解。

常用的命令行交互庫(kù)

在開(kāi)發(fā)腳手架項(xiàng)目時(shí),如果需要命令行交互去獲取一些信息,最常用的庫(kù)是inquirer,其使用也非常簡(jiǎn)單:下面給出簡(jiǎn)單的示例:

var inquirer = require('inquirer');
inquirer.prompt([
  {
    type: 'list',
    name: 'projectType',
    message: '請(qǐng)選擇初始化類(lèi)型',
    default: TYPE_PROJECT,
    choices: [
      {
        name: '項(xiàng)目',
        value: TYPE_PROJECT,
      },
      {
        name: '組件',
        value: TYPE_COMPONENT,
      },
    ],
  },
]);
// 詢(xún)問(wèn)項(xiàng)目的基本信息
inquirer.prompt([
  {
    type: 'input',
    name: 'projectName',
    message: '請(qǐng)確認(rèn)項(xiàng)目名稱(chēng)',
    default: this.projectName,
    validate: function(v) {
      const done = this.async();
      setTimeout(function() {
        // 校驗(yàn)規(guī)則:
        // 1. 首字母必須為英文字符
        // 2. 尾字母必須為英文或數(shù)字,不能為字符
        // 3. 字符僅允許"-_"
        // 4. 長(zhǎng)度必須大于等于1
        if (!/^[a-zA-Z][\w-]{0,}[a-zA-Z0-9]$/.test(v)) {
          // Pass the return value in the done callback
          done('請(qǐng)輸入合法的項(xiàng)目名稱(chēng)');
          return;
        }
        // Pass the return value in the done callback
        done(null, true);
      }, 0);
    }
  },
  {
    type: 'input',
    name: 'projectVersion',
    message: '請(qǐng)輸入項(xiàng)目版本號(hào)',
    default: '1.0.0',
    validate: function(v) {
      const done = this.async();
      setTimeout(() => {
        if (!semver.valid(v)) {
          done('請(qǐng)輸入合法的版本號(hào)');
          return;
        }
        done(null, true);
      }, 0)
    },
    filter: function(v) {
      if (!!semver.valid(v)) {
        return semver.valid(v)
      } else {
        return v
      }
    }
  }
])

開(kāi)發(fā)命令行工具常用庫(kù)或API

readline

readline是node提供的原生API,它可以幫助我們實(shí)現(xiàn)命令行交互,如下是一個(gè)簡(jiǎn)單的案例:

const readline = require('node:readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});
rl.question('What do you think of Node.js? ', (answer) => {
  // TODO: Log the answer in a database
  console.log(`Thank you for your valuable feedback: ${answer}`);
  rl.close();
});

運(yùn)行這個(gè)代碼就會(huì)出現(xiàn)命令行輸入的效果,輸入完成回車(chē)會(huì)在回調(diào)里拿到輸入的內(nèi)容,調(diào)用rl.close()會(huì)結(jié)束交互。

ansi-escapes

ANSI轉(zhuǎn)義序列是用于在視頻文本終端和終端模擬器上控制光標(biāo)位置、顏色、字體樣式和其他選項(xiàng)的帶內(nèi)信令的標(biāo)準(zhǔn)。某些字節(jié)序列(大多數(shù)以ASCII轉(zhuǎn)義字符和方括號(hào)字符開(kāi)始)嵌入到文本中。終端將這些序列解釋為命令,而不是逐字顯示的文本。
當(dāng)我們想改變命令行的文字樣式、位置等就可以使用這個(gè)標(biāo)準(zhǔn),比如我想打印紅色的文字,那么我會(huì)這樣寫(xiě):

console.log('\x1B[31m%s\x1B[0m', 'I am red');

\x1B[是固定寫(xiě)法,31表示紅色字體背景,m表示設(shè)置代碼后面字符的顏色和樣式,%s是log方法第二個(gè)參數(shù)的占位符,0m表示打印完后還原正常樣式。執(zhí)行的效果如下

除了顏色當(dāng)然還可以設(shè)置其他樣式,例如下劃線(xiàn)等,替換code即可,具體可以參考:ANSI escape code - HandWiki 里的code表。

// 打印紅色文字
console.log('\x1B[31m%s\x1B[0m', 'I am red');
// 打印文字添加下劃線(xiàn)
console.log('\x1B[4m%s\x1B[0m', 'underline');
// 紅色文字下劃線(xiàn)
console.log('\x1B[31m\x1B[4m%s\x1B[0m', 'I am red and underline');

除了樣式還可以處理光標(biāo)位置,比如想打印完一行后隔兩行再繼續(xù)打印,則可以這樣寫(xiě):

// 紅色文字下劃線(xiàn)
console.log('\x1B[31m\x1B[4m%s\x1B[0m', 'I am red and underline');
// cursor 移動(dòng)
console.log('\x1B[2B%s\x1B[0m', 'I am red and underline');

將m換成B,B前面的數(shù)字表示要空幾行,效果如下:

還有更多的可能你想要的效果,可以查看 ANSI escape code - HandWiki

rxjs

RxJS 是一個(gè)用于處理異步編程的 JavaScript 庫(kù),目標(biāo)是使編寫(xiě)異步和基于回調(diào)的代碼更容易。

自己開(kāi)發(fā)一個(gè)命令行列表選擇功能

流程圖

代碼實(shí)現(xiàn)

首先模仿inquirer的使用方式搭一個(gè)簡(jiǎn)單的架子:

const option = {
  type: 'list',
  name: 'name',
  message: 'select a name',
  choices: [
    { name: 'sam', value: 'sam' },
    { name: 'tom', value: 'tom' },
    { name: 'jerry', value: 'jerry' }
  ]
}
function prompt(option) {
  return new Promise((resolve, reject) => {})
}
prompt(option).then((answer) => {
  console.log(answer)
})

然后實(shí)現(xiàn)一個(gè)List類(lèi),由它來(lái)做具體的實(shí)現(xiàn):

const { EventEmitter } = require('events');
const rl = require('readline');
const MuteStream = require('mute-stream');
const { fromEvent } = require('rxjs');
const ansi = require('ansi-escapes');
class List extends EventEmitter {
  constructor(option) {
    super();
    this.name = option.name;
    this.message = option.message;
    this.choices = option.choices;
    this.input = process.stdin;
    // 通過(guò)mute-stream來(lái)實(shí)現(xiàn)控制臺(tái)的輸入輸出
    const ms = new MuteStream();
    ms.pipe(process.stdout);
    this.output = ms;
    // 創(chuàng)建readline接口
    this.rl = rl.createInterface({
      input: this.input,
      output: this.output,
    });
    // 默認(rèn)選中
    this.selected = option.default || 0;
    this.height = this.choices.length + 1;
    // 監(jiān)聽(tīng)keypress事件
    this.keypress = fromEvent(this.rl.input, 'keypress').subscribe(
      this.onKeypress
    );
    // 是否選擇完畢
    this.done = false;
  }
  // 鍵盤(pán)事件
  onKeypress = (keyMap) => {
    // 獲取key
    const key = keyMap[1];
    if (key.name === 'up') { // 上鍵點(diǎn)擊
      if (this.selected > 0) {
        this.selected--;
      }
    } else if (key.name === 'down') { // 下鍵點(diǎn)擊
      if (this.selected < this.choices.length - 1) {
        this.selected++;
      }
    } else if (key.name === 'return') { // 回車(chē)點(diǎn)擊
      this.done = true;
    }
    this.render();
    // 完成選擇后退出
    if (this.done) {
      this.close()
      this.emit('exit', this.choices[this.selected])
    }
  }
  render() {
    // 解除mute狀態(tài)
    this.output.unmute();
    // 清除控制臺(tái)
    this.clean();
    // 寫(xiě)入list內(nèi)容
    this.output.write(this.getContent());
    // 開(kāi)啟mute狀態(tài) 限制用戶(hù)不可輸入
    this.output.mute();
  }
  getContent() {
    if (this.done) {
      return `\x1B[1m${this.message} \x1B[22m\x1B[36m${this.choices[this.selected].name}\x1B[39m\n`;
    } else {
      const title = `\x1B[32m?\x1B[39m \x1B[1m${this.message}(use arrow keys)\x1B[22m\n`;
      const list = this.choices.map((item, index) => {
        // 選中的選項(xiàng)前面加上?
        if (index === this.selected) {
          return `\x1B[36m? ${item.name}\x1B[39m`;
        }
        return `  ${item.name}`;
      });
      return title + list.join('\n');
    }
  }
  // 清除控制臺(tái)
  clean() {
    const emptyLines = ansi.eraseLines(this.height);
    this.output.write(emptyLines);
  }
  close() {
    this.output.unmute();
    this.rl.output.end();
    this.rl.pause();
    this.rl.close();
    this.keypress.unsubscribe();
  }
}

然后使用List

function prompt(option) {
  return new Promise((resolve, reject) => {
    try {
      const list = new List(option);
      // 渲染列表
      list.render();
      list.on('exit', (answer) => {
        resolve(answer);
      });
    } catch (error) {
      reject(error);
    }
  });
}
prompt(option).then((answer) => {
  console.log('answer', answer);
});

運(yùn)行效果就是這樣:

到此這篇關(guān)于淺析node命令行交互原理的文章就介紹到這了,更多相關(guān)node 命令行交互內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • nodejs連接mongodb數(shù)據(jù)庫(kù)實(shí)現(xiàn)增刪改查

    nodejs連接mongodb數(shù)據(jù)庫(kù)實(shí)現(xiàn)增刪改查

    本篇文章主要結(jié)合了nodejs操作mongodb數(shù)據(jù)庫(kù)實(shí)現(xiàn)增刪改查,包括對(duì)數(shù)據(jù)庫(kù)的增加,刪除,查找和更新,有興趣的可以了解一下。
    2016-12-12
  • 淺談node的事件機(jī)制

    淺談node的事件機(jī)制

    本篇文章主要介紹了node的事件機(jī)制,本文通過(guò)實(shí)現(xiàn)一個(gè)簡(jiǎn)易的、具有發(fā)布/訂閱模式的事件機(jī)制,以此來(lái)理清EventEmitter類(lèi)的實(shí)現(xiàn)思路,有興趣的可以了解一下
    2017-10-10
  • npm更新命令更新最新版本的實(shí)現(xiàn)方式

    npm更新命令更新最新版本的實(shí)現(xiàn)方式

    這篇文章主要介紹了npm更新命令更新最新版本的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • NodeJS設(shè)計(jì)模式總結(jié)【單例模式,適配器模式,裝飾模式,觀察者模式】

    NodeJS設(shè)計(jì)模式總結(jié)【單例模式,適配器模式,裝飾模式,觀察者模式】

    這篇文章主要介紹了NodeJS設(shè)計(jì)模式,結(jié)合實(shí)例形式總結(jié)分析了nodejs單例模式,適配器模式,裝飾模式,觀察者模式的概念、原理與具體實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-09-09
  • nodejs實(shí)現(xiàn)bigpipe異步加載頁(yè)面方案

    nodejs實(shí)現(xiàn)bigpipe異步加載頁(yè)面方案

    本文給大家分享的是使用nodejs結(jié)合bigpipe實(shí)現(xiàn)異步加載頁(yè)面的方案,非常的實(shí)用,也是以后前端性能優(yōu)化的一個(gè)方向,希望大家能夠喜歡。
    2016-01-01
  • nodejs下打包模塊archiver詳解

    nodejs下打包模塊archiver詳解

    這篇文章主要介紹了nodejs下打包模塊archiver的使用方法,非常簡(jiǎn)單實(shí)用,這里推薦給有需要的小伙伴。
    2014-12-12
  • 詳解使用nvm安裝node.js

    詳解使用nvm安裝node.js

    本篇文章主要介紹了詳解nvm安裝node.js,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • nodejs微信掃碼支付功能實(shí)現(xiàn)

    nodejs微信掃碼支付功能實(shí)現(xiàn)

    本片文章通過(guò)代碼示例給大家詳細(xì)講述了如何用nodejs寫(xiě)出微信掃碼支付這個(gè)功能,有興趣的朋友可以參考下。
    2018-02-02
  • 詳解IWinter 一個(gè)路由轉(zhuǎn)控制器的 Nodejs 庫(kù)

    詳解IWinter 一個(gè)路由轉(zhuǎn)控制器的 Nodejs 庫(kù)

    本篇文章主要介紹了詳解IWinter 一個(gè)路由轉(zhuǎn)控制器的 Nodejs 庫(kù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • NodeJS感知和控制自身進(jìn)程的運(yùn)行環(huán)境和狀態(tài)

    NodeJS感知和控制自身進(jìn)程的運(yùn)行環(huán)境和狀態(tài)

    NodeJS可以感知和控制自身進(jìn)程的運(yùn)行環(huán)境和狀態(tài),也可以創(chuàng)建子進(jìn)程并與其協(xié)同工作,這使得NodeJS可以把多個(gè)程序組合在一起共同完成某項(xiàng)工作,并在其中充當(dāng)膠水和調(diào)度器的作用,和進(jìn)程管理相關(guān)的API單獨(dú)介紹起來(lái)比較枯燥,這里從一些典型的應(yīng)用場(chǎng)景出發(fā)
    2024-01-01

最新評(píng)論

新宾| 广德县| 海阳市| 滦平县| 林口县| 永胜县| 门源| 花莲县| 安平县| 闻喜县| 金秀| 宣威市| 淮北市| 石家庄市| 读书| 隆回县| 卢龙县| 湖南省| 阿拉善右旗| 呼和浩特市| 开化县| 汉中市| 广丰县| 堆龙德庆县| 潮安县| 阿克苏市| 潮州市| 巴里| 高陵县| 朝阳县| 尚志市| 永靖县| 贵南县| 鄂托克旗| 云和县| 桃江县| 会宁县| 鄂托克旗| 莲花县| 苍南县| 淮南市|