HTML頁面生成器:使用JavaScript和Node創建CLI
在第 42 期的文章: 從零開始使用JavaScript製作自己的命令行(CLI工具) 中我們介紹如何從零開始製作CLI,算是一個入門前傳,知道了怎麼製作CLI後今天更進一步。
在這篇文章中,我們將構建一個簡單的CLI,允許用戶生成HTML頁面。我們首先要生成一個標準的空白頁面,然後讓用戶輸入參數,比如文件名和標題,先通過選項,然後通過提示問題讓用戶輸入參數。
創建 Hello World CLI
創建用於編寫CLI的文件夾。我將其命名爲 html-generator-cli 。打開一個終端,然後在此文件夾中運行:
npm init
該命令會有幾個問題要問你,順便說一下,這正是我們最終希望在空白HTML頁面生成器中包含的內容。這將在文件夾中生成 package.json
文件:
我們需要創建包的 index.js
文件作爲入口在package.json中引入。在這個文件中,寫入下面代碼:
console.log('Hello World!');
現在我們需要創建運行這段代碼的命令。
{ "name": "html-generator-cli", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "bin": "index.js" }
將最後一行添加到package.json中。現在,我們可以測試我們非常簡單的CLI。在項目文件夾中局安裝我們新創建的包到本機:
npm install -g ../html-generator-cli
打開一個新終端並運行:
html-generator-cli
如果您使用Windows,現在應該會看到“Hello World!”。在您的終端中。如果您使用的是基於UNIX的操作系統,則應該得到一個錯誤,可能與語法錯誤和意外的token有關。我本人用的是Mac,結果人如下
這是因爲與Windows不同,基於UNIX的系統不關心文件的擴展名(此處爲“.js”),因此不知道使用哪種語言。我們必須告訴系統使用Node運行腳本。爲此,我們在文件的開頭添加一條註釋行:
#!/usr/bin/env node console.log('Hello World!');
創建一個空白的HTML頁面
我們要創建一個CLI來生成HTML文件,爲此,我們將使用Node.js文件系統模塊。該模塊是Node內置模塊,提供與文件系統交互的API,也就是說可以創建、讀取、修改和刪除文件。我們只需要使用文件系統模塊的 writeFile
方法即可,該方法允許你創建文件。
#!/usr/bin/env node const fs = require('fs'); const html = `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Title</title> </head> <body> </body> </html>`; fs.writeFile('index.html', html, error => { if (error) { console.log(error); } });
如果您再次在終端中保存並運行 html-generator-cli
,現在應該在文件夾中看到一個 index.html
文件。
將參數傳遞給代碼
現在我們生產的文件名和HTML中的 title
標籤內容是寫死的,我們應該可以將文件名和標題作爲參數傳遞給CLI。要傳遞參數,你只需在命令之後寫上參數,然後這些參數就可以在一個名爲 argv
的變量中提供給進程。
在代碼中編寫如下代碼:
const args = process.argv; console.log(args);
並在終端中運行它:
html-generator-cli hello haha
然後,你應該在控制檯中看到一個包含參數作爲字符串的數組:
傳遞的參數在數組的最後兩項,我們只需要使用數組的 slice(2)
方法即可拿到。我們決定第一個輸入參數是文件名(不帶HTML擴展名),第二個參數將是HTML頁面的標題。這些參數都不是必需的,如果沒有提供名稱和標題,則我們將文件稱爲index.html,標題爲“Title”。
#!/usr/bin/env node const fs = require('fs'); const args = process.argv.slice(2); let fileName = args[0] ? `${args[0]}.html` : 'index.html'; let title = args[1] || 'Title'; const html = `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>${title}</title> </head> <body> </body> </html>`; fs.writeFile(fileName, html, error => { if (error) { console.log(error); } });
我們保持簡單,不驗證用戶輸入的情況,用戶可能會給該文件指定了無效的名稱,這是你在實際工作中必須驗證的內容。
現在,你可以在終端中嘗試以下操作:
html-generator-cli page "new generator"
結果
使用參數選項
先前的方法易於實現,但有一些缺點:用戶必須知道期望哪些參數以及以什麼順序。如果他不想給出文件名,他也沒有辦法給出標題,我們可以通過創建選項來改善這一點。
與其一個接一個地寫參數,我們可以構建我們的CLI,讓用戶輸入類似於這樣的文件名和/或標題。
html-generator-cli --file-name page --html-title "new generator"
寫起來有點長,但是用戶更清楚他給出的參數是什麼,順序不再起作用,你可以給出一個標題,即使你沒有給出任何文件名。
const args = process.argv; const FILE_NAME_OPTION = '--file-name'; const HTML_TITLE_OPTION = '--html-title'; const fileNameOptionIndex = args.findIndex(arg => arg === FILE_NAME_OPTION); const htmlTitleOptionIndex = args.findIndex(arg => arg === HTML_TITLE_OPTION); const fileNameOption = fileNameOptionIndex > -1 && args[fileNameOptionIndex + 1]; const titleOption = htmlTitleOptionIndex > -1 && args[htmlTitleOptionIndex + 1]; let fileName = fileNameOption ? `${fileNameOption}.html` : 'index.html'; let title = titleOption || 'Title';
現在,我們在參數數組 args
中獲得選項 --file-name
或 --html-title
的索引。如果存在一個選項,那麼要給文件名或標題的值就是參數數組中 --file-name
或 --html-file
之後的元素。如果不存在選項,則其索引將爲 -1
。如果此索引爲 -1
或參數數組中該選項之後沒有任何值,我們分別爲文件名或標題提供默認值。其餘代碼未更改。
你可以運行新的CLI,如果沒有選擇,它將創建標題爲“Title”的index.html文件。如果你編寫一個選項但忘記提供一個值,它將也提供默認值。如果你正確地使用給定的選項編寫命令,那麼它應該創建一個具有正確名稱和正確HTML標題的文件。
html-generator-cli --file-name hello --html-title "CLI helloworld!"
效果
同樣,在實際的CLI中,你會希望多檢查一些輸入,首先要確保用戶輸入的值是有效的,但也要在缺失值或選項出現兩次的情況下警告他們。
向用戶詢問參數
使用選項已經是一種改進了,但是它仍然需要用戶知道他可以傳遞什麼參數以及使用哪個標記。當你初始化你的npm項目時,你可以通過很多東西作爲選項。CLI會直接問您一些問題,因此您無需閱讀文檔即可瞭解如何提供項目名稱,版本等信息。
要從控制檯讀取用戶輸入,我們需要Node(自版本7)提供的模塊 readline
。你可以使用以下代碼在終端中對其進行測試:
const readline = require('readline'); const interface = readline.createInterface({ input: process.stdin, output: process.stdout }); interface.question('你叫什麼名字?', answer => { console.log(`Hello ${answer}`); interface.close(); });
效果如下
爲了生成我們的HTML頁面,我們首先要詢問文件名,然後詢問標題。如果用戶沒有輸入任何內容,我們將獲得默認值。我們向用戶顯示默認值是什麼,以便在默認值正確的情況下可以跳過該問題。
#!/usr/bin/env node const fs = require('fs'); const readline = require('readline'); let fileName = 'index.html'; let title = 'Title'; const interface = readline.createInterface({ input: process.stdin, output: process.stdout }); interface.question(`File name (${fileName}): `, answer => { if (answer && answer.length) { fileName = `${answer}.html`; } interface.question(`HTML title (${title}): `, answer => { if (answer && answer.length) { title = answer; } interface.close(); const html = `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>${title}</title> </head> <body> </body> </html>`; fs.writeFile(fileName, html, error => { if (error) { console.log(error); } }); }); });
如果你在終端中運行它,將會詢問兩個問題。
用戶不必瞭解您的CLI選項,所有重要的事情都可以直接詢問。但是,你應該只以這種方式詢問主要配置問題,並讓用戶閱讀文檔以瞭解不太常見的選項。
結束
我們使用Node和npm創建了一個簡單的CLI,允許用戶生成一個空白的HTML文件,是不是非常簡單?你可以通過添加新選項並驗證用戶輸入來改進此示例。
推薦閱讀
溫故知新 | Vue.js進階必會,編寫你的第一個Vue.js插件
從零開始使用JavaScript製作自己的命令行(CLI工具)
感謝您的閱讀和關注,看完三件事:
如果對你有幫助,幫忙文章右下角點個 在看 如果有什麼問題歡迎 留言 交流,還可以 轉發 ,這是對作者最大的幫助。