我們開發時使用vue的話,一般不會是自己去配置相關的配置,而是會直接使用vue-cli工具,使用react時會用create-react-app,這裏我們來試着搭建一個自己的腳手架工具
就我現在所會的搭建方法,
- 將模板放到github倉庫中,通過執行命令來將github上的代碼拉取到本地
- 使用yeoman-generator,將模板放在generator中,通過yo來將generator中的模板拉取到本地
所以簡單來說,就是將代碼放到某個地方,在需要的時候將代碼拉取到本地,但除了將相關文件拉取到本地外,用過腳手架的同學肯定知道,在初始化的時候,腳手架會提出一些問題,根據我們輸入選擇的不同,最終的配置也有所不同,所以腳手架並非簡單的git clone和yo <name>就可以解決的
第二種搭建的方法我之前寫的一個npm包generator-wxfile已經有用過了,在1.1.2版本中通過使用yeoman-generator來拉取文件,使用co-prompt來向用戶提出配置問題,根據用戶鍵入將相應的文件拉取到本地,並且修改文件中部分內容
這裏我要採用第一種搭建的方法
這裏我要用到幾個npm包
inquirer:用於向用戶提出問題和獲取回答
chalk:改變命令行打印內容的樣式
child_process:用於執行命令行的指令
commander:用於定義自己的命令
初始化腳手架
這裏首先初始化自己的腳手架
mkdir scav-cli
cd scav-cli
npm init
初始化之後,在package.json中把相應的開發依賴寫入
"dependencies": {
"chalk": "^3.0.0",
"child_process": "^1.0.2",
"commander": "^4.1.1",
"inquirer": "^7.0.4"
},
npm i
將相關的npm包下載到node_modules中,接下來就開始腳手架的相關搭建,在bin文件夾中創建scav.js文件用於定義相關命令
定義相關命令
首先將定義腳手架的文件路徑,將相關的依賴包引入,定義腳手架的版本
#!/usr/bin/env node --harmony
'use strict'
// 定義腳手架的文件路徑,__dirname是當前文件所在的路徑
process.env.NODE_PATH = __dirname + '/../node_modules/'
const program = require('commander')
// 獲取package.json中的version來做爲項目的版本號
program.version(require('../package').version)
// 定義腳手架的用法,在program.help方法中會顯示
program.usage('<command>')
給腳手架定義初始化的命令
/*
command爲執行的命令
description爲命令的描述
alias爲簡寫
action爲命令相應的操作
*/
program
.command('init')
.description('init a vue-based project')
.alias('i')
.action(()=>{
console.log('我是初始化的方法')
})
// program.parse(arguments)會處理參數,沒有被使用的選項會被存放在program.args數組中
program.parse(process.argv)
在根目錄下執行下面的命令
node bin/scav init
出現如圖情況就說明配置成功
爲了在全局使用,在package.json中添加bin相關配置
"bin": {
"scav": "bin/scav.js"
},
然後使用npm link鏈接到全局,就可以直接在命令行使用scav init了,如圖
在最後添加命令沒有被定義的操作
// 如果有選項被放在program.args,即沒有被program.parse處理,則默認使用program.help()將npm包可以執行的命令打印出來
// 可以通過program.on('--help', function(){})來自定義help
if (program.args.length) {
program.help()
}
我們可以在判斷之前將program.args打印出來
console.log('program.args: ', program.args);
可以看到,init被處理了,所以program.args裏沒有init,而ttt沒被定義,沒有被處理,所以被寫入program.args中
編寫具體操作
接下來就詳細定義相關的初始化方法了,我們在根目錄下創建一個command文件夾用來存放命令相關操作的文件,創建一個init.js來寫初始化相關操作
接下來編寫init.js,引入相關的依賴包,導出一個方法供scav.js調用
// init.js
const inquirer = require('inquirer')
const chalk =require('chalk')
const {exec} = require('child_process')
chalk.level = 3 // 設置chalk等級爲3
module.exports = ()=>{
console.log(chalk.green('開始初始化文件'))
console.log(chalk.gray('初始化中...'))
console.log(chalk.green('初始化完成'))
}
在init.js文件中調用init.js文件
program
.command('init')
.description('init a vue-based project')
.alias('i')
.action(()=>{
require('../command/init.js')()
})
再執行初始化命令試試,如圖就成功了
接下來使用inquirer來提問用戶項目名,如果項目名爲空,則給出提示並讓用戶重新輸入
module.exports = ()=>{
console.log(chalk.green('開始初始化文件'))
inquirer.prompt([{
type:'input', // 問題類型爲填空題
message:'your projectName:', // 問題描述
name:'projectName', // 問題對應的屬性
validate:(val)=>{ // 對輸入的值做判斷
if(val===""){
return chalk.red('項目名不能爲空,請重新輸入')
}
return true
}
}]).then(answer=>{
console.log(chalk.gray('初始化中...'))
console.log(chalk.green('初始化完成'))
})
}
效果如圖
接下來通過child_process中的exec來執行git clone命令
module.exports = ()=>{
inquirer.prompt([{
type:'input', // 問題類型爲填空題
message:'your projectName:', // 問題描述
name:'projectName', // 問題答案對應的屬性,用戶輸入的內容被存儲在then方法中第一個參數對應的該屬性中
validate:(val)=>{ // 對輸入的值做判斷
if(val===""){
return chalk.red('項目名不能爲空,請重新輸入')
}
return true
}
}]).then(answer=>{ // 通過用戶的輸入進行各種操作
console.log(chalk.green('開始初始化文件\n'))
console.log(chalk.gray('初始化中...'))
const gitUrl = 'https://github.com/QZEming/vue-temp.git'
exec(`git clone ${gitUrl} ${answer.projectName}`,(error,stdout,stderr)=>{
if (error) { // 當有錯誤時打印出錯誤並退出操作
console.log(chalk.red(error))
process.exit()
}
console.log(chalk.green('初始化完成'))
process.exit() // 退出這次命令行操作
})
})
}
出現如圖情況就配置成功了
發現本地多了一個文件夾
修改生成的文件
但是打開package.json,發現name是vue-temp,這可不符合我們的預期,所以我們要引入一個fs模塊,通過fs模塊來讀寫這個package.json,將之前輸入的項目名寫入,通過process.cwd()來獲取當前命令行執行的路徑,代碼如下
const fs = require('fs')
module.exports = ()=>{
inquirer.prompt([{
type:'input', // 問題類型爲填空題
message:'your projectName:', // 問題描述
name:'projectName', // 問題答案對應的屬性,用戶輸入的內容被存儲在then方法中第一個參數對應的該屬性中
validate:(val)=>{ // 對輸入的值做判斷
if(val===""){
return chalk.red('項目名不能爲空,請重新輸入')
}
return true
}
}]).then(answer=>{ // 通過用戶的輸入進行各種操作
console.log(chalk.green('開始初始化文件\n'))
console.log(chalk.gray('初始化中...'))
const gitUrl = 'https://github.com/QZEming/vue-temp.git'
exec(`git clone ${gitUrl} ${answer.projectName}`,(error,stdout,stderr)=>{ // 克隆模板並進入項目根目錄
if (error) { // 當有錯誤時打印出錯誤並退出操作
console.log(chalk.red('拷貝文件失敗'))
process.exit()
}
fs.readFile(`${process.cwd()}/${answer.projectName}/package.json`,(err,data)=>{
if(error){
console.log(chalk.red('讀取文件失敗'))
process.exit()
}
data= JSON.parse(data.toString())
data.name = answer.projectName
fs.writeFile(`${process.cwd()}/${answer.projectName}/package.json`,JSON.stringify(data,"","\t"),err=>{
if(err){
console.log(chalk.red('寫入文件失敗'))
process.exit()
}
console.log(chalk.green('初始化完成'))
process.exit() // 退出這次命令行操作
})
})
})
})
}
打開package.json發現與我們輸入的項目名一樣即修改成功
npm發佈的相關內容可見前端工程化 發佈一個自動生成微信開發文件的npm包
我已經發布了這個腳手架到npm上,相關代碼放在github上,也可以npm i scav-cli -g試試相關的內容