命令行上簡單的輸入一行代碼,而後立刻就創建了一個模板工程,這種方式想一想都會以爲很酷。例如使用vue-cli工具的vue命令能夠建立一個高配的模板工程。做爲第一行代碼咱們就先不玩那麼高深的,就來個hello world
。html
咱們能夠新建一個項目目錄叫作test,而後進入該目錄下npm init一路執行回車,最後在項目目錄下新建一個bin文件夾,建立一個hello.js文件,而後寫上:前端
#!/usr/bin/env node console.log("hello world");
修改package.json文件:vue
{ "name": "test", "bin": { "test": "bin/hello.js" } }
而後執行 npm link 命令:node
$ npm link ... C:\Users\Administrator\AppData\Roaming\npm\test -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\test\bin\hello.js C:\Users\Administrator\AppData\Roaming\npm\node_modules\test -> E:\github-code\test
命令行執行test,會打印出:hello world。git
咱們的hello world程序跑完了,這裏重點聊聊兩個問題:github
#!/usr/bin/env node
在這裏有什麼做用?vue-cli
npm link 到底在這裏有什麼做用?npm
要理解這兩個問題,首先咱們要知道操做系統中都會有一個 PATH 環境變量,當系統調用一個命令的時候,就會在PATH變量中註冊的路徑中尋找,若是註冊的路徑中有就調用,不然就提示命令沒找到。咱們能夠經過process.env
獲取本機系統中全部的環境變量。json
對於模塊的載入及緩存機制能夠分爲如下幾中狀況:segmentfault
載入內置模塊(A Core Module)
載入文件模塊(A File Module)
載入文件目錄模塊(A Folder Module)
載入node_modules裏的模塊
自動緩存已載入模塊
這裏咱們重點關注的是載入node_modules裏的模塊。若是模塊名不是路徑,也不是內置模塊,Node將試圖去當前目錄的node_modules文件夾裏搜索。若是當前目錄的node_modules裏沒有找到,Node會從父目錄的node_modules裏搜索,這樣遞歸下去直到根目錄。
咱們經過全局命令安裝的模塊會保存在全局目錄下,如:
npm install -g vue-cli
咱們能夠在{prefix}/node_modules
目錄下找到vue-cli
文件夾,這個就包含了vue-cli的包。
對於不一樣的系統,{prefix}
值不一樣,這裏咱們可使用npm prefix命令獲取:
npm prefix -g
固然咱們也能夠經過npm config命令的get和set方法操做這個路徑。
那麼再去理解上面的兩個問題就簡單了,#!/usr/bin/env node
主要是幫助腳本找到node的腳本解釋器。npm link的做用至關因而將咱們的工程進行了全局安裝,可是不一樣的是咱們能夠在命令行進行使用package.json文件中bin字段下的命令。如上面的test工程經過 npm link,這一步能夠將本地目錄安裝到模塊全局目錄{prefix}/node_modules
下,而且會在{prefix}
文件夾下生成test文件和test.cmd文件。另外最關鍵的時候會創建連接,當本地目錄如test文件夾變更,全局模塊目錄下的test文件夾也會相應改變,在其餘目錄下調用test命令一樣能夠找到命令。
一樣咱們能夠經過下面的命令卸載模塊:
npm uninstall -g test
node process對象一個提供有關當前Node.js進程的信息和控制的全局對象,在node環境下無需經過require()便可調用。
process.argv屬性返回一個數組,其中包含啓動Node.js進程時傳遞的命令行參數。第一個元素是process.execPath, 若是須要訪問argv [0]的原始值,可使用process.argv0,第二個元素將是要執行的JavaScript文件的路徑, 其他元素將是任何其餘命令行參數。
#!/usr/bin/env node console.log('call %s', process.argv[2]);
而後輸入test hello
,打印出call hello
。
對於命令行參數處理,咱們通常用現成的模塊commander或yargs處理,提供了用戶命令行輸入和參數解析強大功能。這裏咱們就使用輕量級,表達力強大的commander進行處理。
官網:http://tj.github.io/commander...
安裝:
npm install --save commander
commander 特性:
自記錄代碼
自動生成幫助
合併短參數(「ABC」==「-A-B-C」)
默認選項
強制選項
命令解析
提示符
commander API:
exports.Command —— 暴露Command對象
exports.Option —— 暴露Option對象
Option() —— 初始化自定義參數對象,設置「關鍵字」和「描述」
Command() —— 初始化命令行參數對象,直接得到命令行輸入
Command#command() —— 定義命令名稱
Command#arguments() —— 定義頂級命令的參數語法
Command#parseExpectedArgs() —— 解析預期參數
Command#action() —— 註冊命令的回調函數
Command#option() —— 定義參數,須要設置「關鍵字」和「描述」,關鍵字包括「簡寫」和「全寫」兩部分,以」,」,」|」,」空格」作分隔
Command#allowUnknownOption() —— 容許命令行未知參數
Command#parse() —— 解析argv,設置選項和定義時調用命令
Command#description() —— 添加命令描述
Command#alias() —— 設置命令別名
Command#usage() —— 設置/獲取用法
node提供了標準的命令行輸入的API——readline。
const readline = require('readline'); const rl = readline.createInterface(process.stdin, process.stdout); rl.question('what is your name? ', function(answer){ console.log(`name is ${answer}`); rl.close(); });
注意:當調用該代碼時,Node.js 程序不會終止,直到 readline.Interface 被關閉,由於接口在等待 input 流中要被接收的數據。
配合異步流程控制典型的co 模塊使用:
let readlinePrompt = function (query) { return new Promise(function (resolve, reject) { rl.question(query, function(answer){ resolve(answer); }); }); }; co(function *() { var template = yield prompt('what is template-name? '); var project = yield prompt('what is project-name? '); console.log(`template-name is ${template}`); console.log(`template-name is ${project}`); rl.close(); })
爲了簡便咱們通常都會使用co-prompt或者Inquirer.js之類的模塊。如使用co-prompt模塊咱們能夠這樣寫:
const prompt = require('co-prompt'); co(function *() { var template = yield prompt('what is template-name? '); var project = yield prompt('what is project-name? '); console.log(`template-name is ${template}`); console.log(`template-name is ${project}`); })
參考vue-cli中下載git模板項目的方法,這裏主要是使用了download-git-repo模塊,以及使用ora模塊顯示下載狀態。
let templateName = program.args[1] let templateDir = path.join(home, '.plus-templates', templateName.replace(/\//g, '-')) let clone = program.clone || false function downloadAndGenerate(template) { const spinner = ora('downloading template').start(); download(template, templateDir, { clone: clone }, function (err) { spinner.stop(); if (err) logger.fatal('Failed to download repo ' + template + ': ' + err.message.trim()) // generate }) }
Node.js的模塊載入方式與機制
教你從零開始搭建一款前端腳手架工具
異步流程控制:7 行代碼學會 co 模塊
【譯】使用Node.js建立命令行腳本工具