命令行交互核心實(shí)現(xiàn)
- 核心目標(biāo):實(shí)現(xiàn)命令行行交互,如List
- 命令行的交互呢比命令行的渲難度要更大,因?yàn)樗婕暗募夹g(shù)點(diǎn)會(huì)會(huì)更多
- 它涉及以下技術(shù)點(diǎn)
- 鍵盤(pán)輸入的一個(gè)監(jiān)聽(tīng) (這里通過(guò)
readline
來(lái)實(shí)現(xiàn)) - 計(jì)算命令行窗口的尺寸
- 清屏
- 光標(biāo)的移動(dòng)
- 輸出流的靜默 (我們輸出的內(nèi)容, 不讓它去輸出到當(dāng)前的這個(gè)終端中)
- 借助輸入輸出流,引出輸入輸出流的一個(gè)監(jiān)聽(tīng)以及事件庫(kù)
events
- ansi escaped code 轉(zhuǎn)義字符
- 鍵盤(pán)輸入的一個(gè)監(jiān)聽(tīng) (這里通過(guò)
- 命行交互其實(shí)是有一定復(fù)雜度的, 在這個(gè)過(guò)程中,最重點(diǎn)的庫(kù)和命行交互最重點(diǎn)庫(kù)是兩個(gè)
readline
和inquirer
inquirer
- inquirer 是一個(gè)命令行交互常用的庫(kù),Weekly Downloads 30,375,340 (動(dòng)態(tài)數(shù)據(jù))
- 作為一個(gè)命令行交互的庫(kù)能做到這個(gè)程度,可以說(shuō)是非常的不簡(jiǎn)單,而且一直在持續(xù)的進(jìn)行維護(hù),目前已經(jīng)達(dá)到9.2.15版本了
- 安裝 $
npm i -S inquirer
- 使用示例1:input類(lèi)型演示
import inquirer from 'inquirer'; inquirer .prompt([ { type: 'input', name: 'yourName', message: 'your name:', } ]) .then((answers) => { console.log(answers); }) .catch((error) => { if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } });
- 這里根據(jù)文檔上的框架結(jié)構(gòu)
- 通過(guò) type, name, message 三個(gè)字段即可完成初始化創(chuàng)建
- 更多,參考文檔:https://www.npmjs.com/package/inquirer#question

- 另外,比較常用的還有
- default 默認(rèn)值字段
- validate 字段是一個(gè)回調(diào)
- 用于對(duì)字段的校驗(yàn),只有校驗(yàn)返回 true的時(shí)候校驗(yàn)才會(huì)結(jié)束
- transformer 字段用于處理信息展示的回調(diào)
- 也就是這個(gè)函數(shù)內(nèi)部返回的值是展示的值
- 返回的值還是之前的 name 字段
- 更多的像是表單中的 placeholder 僅作為展示
- filter 字段是一個(gè)回調(diào)
- 它會(huì)最終改變 answers 最終的結(jié)果
- 會(huì)最終改變 name 字段
- 其他: choice 在匹配 List 列表的時(shí)候會(huì)用到
- 注意,
prompt
方法內(nèi)部接受的是一個(gè)數(shù)組,可以寫(xiě)多個(gè)對(duì)象來(lái)收集數(shù)據(jù) - 使用示例2: 多字段演示
import inquirer from 'inquirer'; inquirer .prompt([ { type: 'input', name: 'yourName', message: 'your name:', default: 'Lee', validate: function(v) { return v === 'Wang' }, transformer: function(v) { return 'your input name: ' + v // 僅作為展示 }, filter: function(v) { return v; // return v + '123' // 改變最終值 } }, { type: 'number', // 這種,在沒(méi)有 validate 的情況下,如果輸入的是非數(shù)字, 會(huì)變成 NaN name: 'num', message: 'your number', }, // ... ]) .then((answers) => { console.log(answers); // 最終打印的是一個(gè)對(duì)象,多個(gè)字段 }) .catch((error) => { if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } });
- 在示例1中已做了詳細(xì)說(shuō)明,這里不再贅述

- 使用示例3: confirm 類(lèi)型演示
import inquirer from 'inquirer'; inquirer .prompt([ { type: 'confirm', // 二選一功能 name: 'choice', message: 'your choice:', default: false, }, ]) .then((answers) => { console.log(answers); }) .catch((error) => { if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } });

- 使用示例4: list 類(lèi)型演示
import inquirer from 'inquirer'; inquirer .prompt([ { type: 'list', // 列表單選 name: 'choice', message: 'your choice:', default: 0, // 這里 default 是 下面choices 的索引 choices: [ {value: 1, name: 'LiLy'}, {value: 2, name: 'Lucy'}, {value: 3, name: 'Lee'}, ] }, ]) .then((answers) => { console.log(answers); }) .catch((error) => { if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } });


- 使用示例5: expend 類(lèi)型演示
import inquirer from 'inquirer'; inquirer .prompt([ { type: 'expand', // 簡(jiǎn)寫(xiě)選擇 name: 'choice', message: 'your choice:', default: 'red', choices: [ {value: 'red', key: 'R'}, {value: 'green', key: 'G'}, {value: 'blue', key: 'B'}, ] }, ]) .then((answers) => { console.log(answers); }) .catch((error) => { if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } });
- 簡(jiǎn)寫(xiě)選擇功能,除了 Rgb 還有一個(gè) h
- 輸入 h 回車(chē),會(huì)得到 help 提示,列出了所有選項(xiàng)
- 輸入 r 回車(chē),會(huì)得到 red, 輸入 g 回車(chē),會(huì)得到 green


- 使用示例6: checkbox 類(lèi)型演示
import inquirer from 'inquirer'; inquirer .prompt([ { type: 'checkbox', // 復(fù)選框 name: 'choice', message: 'your choice:', default: 0, choices: [ {value: 1, name: 'Lily'}, {value: 2, name: 'Lucy'}, {value: 3, name: 'Lee'}, ] }, ]) .then((answers) => { console.log(answers); }) .catch((error) => { if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } });
- 這里提供 a 全選,空格鍵 選中,i 反選的功能
- 默認(rèn),上下箭來(lái)選擇


- 使用示例7: password 類(lèi)型演示
import inquirer from 'inquirer'; inquirer .prompt([ { type: 'password', // 密碼框 name: 'password', message: 'your password:', }, ]) .then((answers) => { console.log(answers); }) .catch((error) => { if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } });

- 使用示例8: editor 類(lèi)型演示
import inquirer from 'inquirer'; inquirer .prompt([ { type: 'editor', // 編輯器 name: 'editor', message: 'your editor text:', }, ]) .then((answers) => { console.log(answers); }) .catch((error) => { if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } });



- 上面中間的這個(gè)類(lèi)似 vim 的界面,會(huì)在一個(gè)緩存文件中,輸入完以后,緩存文件被刪除掉
- 我們輸入的結(jié)果會(huì)被保留下來(lái),如上圖
- 這樣做的好處是在文本編輯器中輸入復(fù)雜的內(nèi)容
readline
- readline,是 nodejs 當(dāng)中的一個(gè)內(nèi)置庫(kù),主要幫我們?nèi)ス芾頂?shù)據(jù)流的
- 命令行當(dāng)中要交互的方式,一定是需要用戶提供一些輸入的
- readline 就可以很好的幫我們?nèi)ヒ淮我淮蔚淖x取這個(gè)輸入流
- 注意,這個(gè)輸入不僅是指我們輸入一些字符,還包含我們鍵盤(pán)上輸入的一切,如上,下,空格,回車(chē)等
- 基本使用
import * as readLine from 'readline'; const rl = readLine.createInterface({ input: process.stdin, output: process.stdout, }); rl.question('your name: ', (answer) => { console.log(answer); rl.close(); // 關(guān)閉讀取流 })

- readline 主要用途是根據(jù)傳入的輸入流逐行讀取信息
- 回車(chē)的時(shí)候,會(huì)認(rèn)為這行輸入結(jié)束,并且把所有輸入的內(nèi)容傳遞到輸出流中進(jìn)行展示
- 這是readline的核心用途
- 如果調(diào)試 readline 源碼,可知,它內(nèi)部會(huì)強(qiáng)制將函數(shù)轉(zhuǎn)換為構(gòu)造函數(shù)
if (!(this instanceof Interface)) { return new Interface(input, output, completer, terminal); }
- 接著是對(duì) StringDecoder的判斷和賦值,這個(gè)也是node的一個(gè)內(nèi)置庫(kù)
if (StringDecoder === undefined) { StringDecoder = require('string_decoder').StringDecoder; }
- 再之后,定義了一些列的參數(shù),調(diào)用了 EventEmitter
EventEmitter.call(this)
- 這個(gè)用途是使用 this 繼承 EventEmitter, this內(nèi)部就會(huì)生成一些列的屬性信息,如 _events, _eventsCount
- 讓當(dāng)前 Interface 實(shí)例具備事件驅(qū)動(dòng)的能力,因?yàn)閚odejs有單線程,非阻塞IO,事件驅(qū)動(dòng)的特性
- 也就是說(shuō)事件驅(qū)動(dòng),在單線程的nodejs中是非常重要的
- 再接著,定義一些參數(shù), 對(duì) input 進(jìn)行判斷,也就是分析 input 參數(shù)
if (input && input.input) { // .... }
- 再往后找,看readline是如何做事件監(jiān)聽(tīng)的
this.output = output; // output: WriteStream 系統(tǒng)輸出流 this.input = input; // input: ReadStream 系統(tǒng)輸入流 // ... emitKeypressEvents(input, this) // 這里就是監(jiān)聽(tīng)用戶在終端中的鍵盤(pán)輸入
- 在 emitKeypressEvents 函數(shù)內(nèi)部,會(huì)調(diào)用一個(gè) emitKeys 的方法
- 這里是核心, 其原理和源碼不在這里進(jìn)行剖析
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-856231.html
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-856231.html
到了這里,關(guān)于前端架構(gòu): 腳手架命令行交互核心實(shí)現(xiàn)之inquirer和readline的應(yīng)用教程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!