從零開始搭建前端腳手架


一、功能設計

每個前端小組都會有自己的獨特的業務場景,從這些業務場景從提取公共部分,並打造一個前端項目模版,是非常有必要的

為了能夠基於這個項目模版快速創建一個新項目,就需要腳手架工具登場

所以這里至少有兩個項目倉庫:前端模版項目、腳手架工具

而對於腳手架工具,它應當具備這樣的功能:輸入一個命令和項目名稱,創建對應的項目目錄,其內容就是模版項目

my-cli create my-project

基於此,腳手架工具的內部邏輯也就很清晰了:

創建一個 create 命令,其行為是拉取模版項目的代碼(用 git clone 即可實現)

為了實現這個命令,我們需要借助一個神奇的工具:commander

 

 

二、Commander

Commander.js 是完整的 node.js 命令行解決方案

可以通過它創建一個 program 對象,作為程序的主體

const { Command } = require('commander'); const program = new Command(); program.version('0.0.1');

commander 常用的功能有創建選項和命令


選項 option 需要基於短線 " - " 聲明,常見的  -h 、 -V  都是選項

每個選項可以定義一個短選項名稱(" - " 后面接單個字符)和一個長選項名稱(" -- "后面接一個或多個單詞)

解析后的選項可以通過 .opts() 方法獲取,從而執行對應的操作

比如創建一個 -d 選項:

program .option('-d, --debug', 'output extra debugging') program.parse(process.argv); const options = program.opts(); if (options.debug) console.log(options);

還可以對選項定義參數及默認值:

program .option('-d, --debug <type>', 'output extra debugging', 'hard')

這里就對 debug 選項定義了 type 參數,並設置其默認值為 hard

用尖括號   <>  定義的參數為必填,用中括號   []  創建的參數為選填


命令 command 可以通過  .command()  創建,並通過鏈式調用 .description() 和 .action() 定義該命令的描述和具體行為

program .command('create <name> [type]') .description('create a new project') .action((name, type) => { console.info('project', name); });

和選項類似,命令也可以通過  <>  和  []  定義參數

除了像上面那樣通過鏈式調用創建命令行為(description、action)之外,還可以用單文件的形式描述命令,不過我沒有跑通


commander 還有更多更強大的功能,這里就不逐一介紹了,詳情可以參考中文文檔

 

 

三、正式開始

學習了 commander 之后,就可以着手腳手架的開發了 

首先創建腳手架工具的目錄,如 my-cli,然后在目錄下創建一個 .gitignore 文件

# Dependency directories node_modules/

接着通過  npm init 創建 package.json

創建之后可以刪除其中的 main 和 scripts,然后安裝 commander

npm install commander --save

然后創建 bin 目錄及 bin/cli.js 文件:

#!/usr/bin/env node const { Command } = require('commander'); const { name, version } = require('../package.json'); const program = new Command(); program.name(name).version(version); program .command("create <project-name>") .description("create a new project") .action((name) => { console.info('project', name); }); program.parse();

頂部的  #!/usr/bin/env node  是告訴操作系統,用 /usr/bin 下的 node 來執行這個腳本

 

一個簡單的 cli 命令就創建好了,可以回到根目錄,用 node 執行 cli.js 試試:

node bin/cli.js create my-project

可以看到 create 命令正常執行 

但直接通過 node + 路徑 的形式運行代碼還是太死板了,可以通過  npm link 將項目掛到全局,就能像正常的腳手架工具那樣用了

首先需要在 package.json 里添加 bin 命令:

{ ... "bin": { "my-cli": "bin/cli.js" } }

這里的 my-cli 是包的名稱,后面的路徑需要寫全后綴

然后在根目錄執行  npm link 就能將 my-cli 鏈接到全局

npm link 是一個很好的本地測試的手段,調試完成后可以通過 npm unlink 卸載

 

 

四、完善 create 命令 

據文檔介紹, commander 支持將命令拆成單文件進行維護,但我沒有搞出來,最后只好加了一層 map 來拆文件

在根目錄下新建一個 command 目錄,並創建 command/create.js 和 command/index.js 兩個文件:

然后將 create 命令的相關邏輯移到 command/create.js 中

// create.js
 function createAction(name) { console.log('project', name); } const create = { alias: 'c', params: '<project-name>', description: 'create a new project', action: createAction, } module.exports = create;

並在 command/index.js 中導出

// index.js

const create = require('./create.js'); module.exports = { create, };

最后來改造 bin/cli.js

#!/usr/bin/env node const { Command } = require('commander'); const { name, version } = require('../package.json'); const commands = require('../command/index.js'); const program = new Command(); program.name(name).version(version); // 創建命令
Reflect.ownKeys(commands).map((name) => { const { params, alias, action, description } = commands[name] || {}; program.command(`${name} ${params || ''}`) .alias(alias) .description(description) .action((...args) => { typeof action === 'function' && action(...args); }) }); program.parse(process.argv);

項目的結構基本成型,接下來完善 create 命令

在文章的開頭就已經分析過了,create 命令只需要做一個事情,就是將項目 clone 到當前目錄

const { exec } = require("child_process"); function createAction(name) { // 這是模板項目的倉庫地址
  const url = "http://github.com/xxx/template.git"; // 克隆項目
  exec(`git clone ${url} ${name}`, (error, stdout, stderr) => { if (error) { console.log(error); process.exit(); } console.log("Success"); process.exit(); }); }

如果模板項目是 github 上的項目,應該沒什么大問題

而如果模板項目是小組內部的 gitlab 項目,就需要放開模板項目的訪問權限,至少讓小組人員都能夠訪問

 

 

五、優化體驗

在完成了 create 命令之后,我們的腳手架工具就可以算是開發完成

但為了更好的體驗,可以借助這些工具加以改造:chalkinquirer

 

1. chalk

它可以在命令行打印彩色文字:

import chalk from 'chalk'; const error = chalk.bold.red; const warning = chalk.hex('#FFA500'); console.log(chalk.blue('Hello world!')); console.log(error('Error!')); console.log(warning('Warning!'));

 

2. inquirer

這是一個讓用戶與命令行交互的工具

它提供了很多 api,讓用戶可以在程序運行的過程中輸入內容,從而影響程序運行的結果

const inquirer = require('inquirer'); inquirer .prompt([ /* Pass your questions in here */ ]) .then((answers) => { console.log('success', answers); }) .catch((error) => { console.log('error'); });

在上面的 prompt 中配置需要用戶輸入/選擇的(表單)內容

比如讓用戶輸入項目名稱:

.prompt([ { type: "input", message: "項目名稱:", name: "projectName", validate: (val) => { // 對輸入的值做判斷
      if (!val || !val.trim()) { return chalk.red("項目名不能為空,請重新輸入"); } else if (val.includes(" ")) { return chalk.red("項目名不能包含空格,請重新輸入"); } return true; }, }, ])

除了這里的 input 類型外,inquirer 還提供了很多交互類型,如單選列表 list、多選 checkbox、確認項 confirm 等

 


在了解了 chalk 和 inquirer 之后,我們就可以進一步改造 create 命令

npm install inquirer chalk --save
// create.js
const inquirer = require("inquirer"); const chalk = require("chalk"); const { exec } = require("child_process"); function createProject(name) { // 這是模板項目的倉庫地址
  const url = "git@github.com:wisewrong/chart-admin.git"; exec(`git clone ${url} ${name}`, (error, stdout, stderr) => { if (error) { console.log(chalk.red(error)); process.exit(); } console.log(chalk.green("Success")); process.exit(); }); } const create = { alias: "c", params: "[project-name]", description: "create a new project", action: (project) => { project ? createProject(project) : inquirer .prompt([ { type: "input", message: "項目名稱:", name: "projectName", validate: (val) => { // 對輸入的值做判斷
                if (!val || !val.trim()) { return chalk.red("項目名不能為空,請重新輸入"); } else if (val.includes(" ")) { return chalk.red("項目名不能包含空格,請重新輸入"); } return true; }, }, ]) .then((answer) => { createProject(answer.projectName); }); }, }; module.exports = create;

麻雀雖小五臟俱全,這樣一個簡單的腳手架就開發完了

如果需要發布到 npm,可以參考我之前的文章《vue-cli 3.x 開發插件並發布到 npm》

后面我也會繼續完善這個腳手架,添加更多的交互,以及展示進度條(專業挖坑,從來不填...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM