Vue-cli打造自己項(xiàng)目的腳手架工具
為什么需要腳手架(命令行工具)?
- 減少重復(fù)性的工作,不再需要復(fù)制其他項(xiàng)目再刪除無關(guān)代碼,或者從零創(chuàng)建一個(gè)項(xiàng)目和文件;
- 根據(jù)交互動(dòng)態(tài)生成項(xiàng)目結(jié)構(gòu)和配置文件等;
- 多人協(xié)作更為方便,不需要再把文件傳來傳去。
實(shí)現(xiàn)思路

image.png
- 項(xiàng)目模板放在github上
- 用戶通過命令交互的方式下載不同的模板
- 經(jīng)過模板引擎渲染定制項(xiàng)目模板
- 模板變動(dòng),只需要更新模板即可,不需要用戶更新腳手架
涉及知識(shí)點(diǎn)及模塊
- NodeJs
基于Node.js開發(fā)命令行工具 - ECMAScript 6
使用最新版本語言進(jìn)行開發(fā) - npm 發(fā)包
npm包的發(fā)布及更新流程 - commander.js
可以自動(dòng)的解析命令和參數(shù),用于處理用戶輸入的命令 - download-git-repo
下載并提取git倉庫,用于下載項(xiàng)目模板 - Inquirer.js
通用的命令行用戶界面集合,用于和用戶進(jìn)行交互 - handlebars.js
模板引擎,將用戶提交的信息動(dòng)態(tài)填充到文件中 - ora
下載過程久的話。可以用于顯示下載中的動(dòng)畫效果 - chalk
可以給終端的字體加上顏色 - log-symbols
可以在終端上顯示出狗√或×等圖標(biāo)
初始化操作
- 初始化
mkdir demo-cli
cd demo-cli
npm init -y // 初始化生成package.json文件
- 新建 index.js 并寫入以下內(nèi)容:
#!/usr/bin/env node
console.log('hello cli');
- 配置package.json 中的bin字段
{
"name": "vue-cli-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"bin": {
"test": "index.js"
}
}
- 執(zhí)行npm link 鏈接命令到全局/執(zhí)行npm unlink 可以建命令移除(在項(xiàng)目根路勁下執(zhí)行)
- 執(zhí)行bin中配置的命令測(cè)試
例如:在命令行輸入命令test可看到對(duì)應(yīng)輸出。
命令行工具參數(shù)設(shè)計(jì)
test -h|--help // 查看使用幫助
test -V|--version // 查看工具的版本號(hào)
test list // 列出所有可用模板
test init <template-name> <project-name> // 基于指定的模板進(jìn)行項(xiàng)目初始化
使用 commander 模塊處理命令行
- 安裝
npm install commander - 使用
#!/usr/bin/env node
// 使用Node開發(fā)命令行工具所執(zhí)行的Javascript腳本必須在頂部加入 #!/usr/bin/env node
const program = require('commander');
const download = require('download-git-repo');
const inquirer = require('inquirer');
const handlebars = require('handlebars');
const fs = require('fs');
const ora = require('ora');
const chalk = require('chalk');
const logSymbols = require('log-symbols')
// 1.獲取用戶輸入命令
// process.argv原生獲取命令行參數(shù)的方式
// console.log(process.argv)
program
.version('0.0.1') // -V 或者 --version 的時(shí)候輸出該版本號(hào)
const templates = {
'tpl-a': {
url: '', // 模板倉庫地址
downloadUrl: 'http://github.com:用戶名/倉庫名#分支名', // 模板下載地址
description: 'a模板',
},
'tpl-b': {
url: '',
downloadUrl: '', // 模板下載地址
description: 'b模板',
},
'tpl-c': {
url: '',
downloadUrl: '', // 模板下載地址
description: 'c模板',
}
}
program
.command('init <template> <project>')
.description('初始化項(xiàng)目模板')
.action((templateName, projectName) => {
const spiner = ora('正在下載模板...').start()
// 根據(jù)模板名下載對(duì)應(yīng)的模板到本地
// download: 第一個(gè)參數(shù)是倉庫下載地址,第二個(gè)參數(shù)是下載路勁
const {downloadUrl} = templates[templateName];
download(downloadUrl, projectName, {clone: true}, (err) => {
if (err) {
spiner.fail();
console.log(logSymbols.error, chalk.red('初始化模板失敗'))
return
}
spiner.succeed(); // 下載成功提示
// 模板文件下載成功
// 1. 把項(xiàng)目下的package.json文件讀取出來
// 2. 使用向?qū)У姆绞讲杉脩糨斎氲闹? // 3. 使用模板引擎把用戶輸入的數(shù)據(jù)解析到package.json文件中
// 4. 解析完畢,把解析之后的結(jié)果重新寫入package.json文件中
inquirer.prompt([
{
type: 'input',
name: 'name',
message: '請(qǐng)輸入項(xiàng)目名稱'
},
{
type: 'input',
name: 'description',
message: '請(qǐng)輸入項(xiàng)目名稱'
},
{
type: 'input',
name: 'author',
message: '請(qǐng)輸入項(xiàng)目名稱'
}
]).then((answers) => {
// 把采集到的用戶輸入的數(shù)據(jù)解析替換到package.json中
const packagePath = `${projectName}/package.json`
const packageContent = fs.readFileSync(packagePath, 'utf8') // 讀取本地文件
const packageResult = handlebars.compile(packageContent)(answers) // 編譯替換
fs.writeFileSync(packagePath, packageResult); // 重寫到本地文件
console.log(logSymbols.success, chalk.yellow('初始化模板成功'))
})
})
});
program
.command('list')
.description('查看所有可用模板')
.action(() => {
for (key in templates) {
console.log(`${key} ${templates[key].description}`)
}
})
program.parse(process.argv);
// 2. 根據(jù)不同的指令執(zhí)行不同的功能操作

image.png
下載模板
- 安裝
npm install download-git-repo -
修改代碼
image.png - download()第一個(gè)參數(shù)就是倉庫下載地址
端口號(hào)后面的'/' 在參數(shù)里要寫成‘:’
#master代表的就是分支名
不同的模板可以放在不同的分支下,更改分支便可以實(shí)現(xiàn)下載不同的模板文件了 - 第二個(gè)參數(shù)是路勁
上面我們直接在當(dāng)前路徑下創(chuàng)建一個(gè)name命名的文件夾存放模板,也可以使用二級(jí)目錄比如test/${name}
命令行交互
- inquirer
一組常見的交互式命令行用戶界面。 - 安裝
npm install inquirer
命令行交互功能可以在用戶執(zhí)行init命令后,向用戶提出問題,接收用戶的輸入并做出相應(yīng)的處理,這里使用inquirer.js來實(shí)現(xiàn)。

image.png
1)問題就放在prompt()中
2)問題的類型為input就是輸入類型
3)name就是作為答案對(duì)象中的key
4)message就是問題了
5)用戶輸入的答案就在answers中
修改模板內(nèi)package.json文件里需要用戶自定義配置的參數(shù),例如:

image.png
在下載模板完成后將用戶輸入的答案渲染到package.json中
- handlebars(模板引擎)
Handlebars.js 是Chris Wanstrath 創(chuàng)建的Mustache 模板語言的擴(kuò)展。 - 安裝
npm i handlebars
// 把采集到的用戶輸入的數(shù)據(jù)解析替換到package.json中const packagePath = `${projectName}/package.json`
onst packageContent = fs.readFileSync(packagePath, 'utf8') // 讀取本地文件const packageResult = handlebars.compile(packageContent)(answers) // 編譯替換
fs.writeFileSync(packagePath, packageResult); // 重寫到本地文件
上面使用node.js的文件模塊fs,將handlebars渲染完成后的模板重新寫入到文件中。
視覺美化
在用戶輸入答案后,開始下載模板,這時(shí)候使用ora來提示用戶正在下載中。
- 安裝ora
npm install ora

image.png
然后通過chalk來為打印信息加上樣式,比如成功信息為綠色,失敗信息為紅色,這樣子會(huì)讓用戶更加容易分解,同時(shí)也讓終端的顯示更加的好看。
- 安裝chalk
npm install ora - 安裝log-symbols
npm install log-symbols
npm 發(fā)包
發(fā)完包之后就能通過npm下載自己的cli
npm install --global 腳手架名
- 打開npmjs.com官網(wǎng)
- 注冊(cè)一個(gè)npm賬號(hào)
- 在npm檢索是否有重名的包名
- 將package.json里的name修改為發(fā)布到npm上的包名(該名字和本地項(xiàng)目名稱無關(guān))
- 打開控制臺(tái),執(zhí)行
npm login,登錄npm - 登錄成功以后,在項(xiàng)目下執(zhí)行
npm publish發(fā)布 - 發(fā)布成功,就可以在本地進(jìn)行安裝測(cè)試了
