腳手架本質上是一個工具,使用腳手架的目的就是擺脫構建工程時重複性的工做,尤爲是當一個工程具備必定通用性時,工程腳手架的意義就更爲突出。它可讓咱們只須要一行命令,就能夠初始化好一項工程,而不用費心費力的去作配置環境,安裝依賴,解決依賴衝突這樣的支線任務,能夠直奔主線任務,早早下班~~vue
開發一個的腳手架,一般須要以下npm包:node
實現的功能:react
STEP1: 打開一個終端,在你喜歡的地方新建一個空項目git
mkdir meo-cli
cd meo-cli
複製代碼
在項目更目錄下執行:github
npm init -y
複製代碼
你會獲得一個package.jsonvue-cli
{
"name": "meo-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
複製代碼
關鍵點來了,在 package.json中加以一個bin字段,在安裝時,npm 會將文件符號連接到 prefix/bin 以進行全局安裝或./node_modules/.bin/本地安裝。這樣,就能夠全局使用了。例如,下面的將meo-cli做爲命令名稱,執行文件是根目錄的index.jsnpm
{
"name": "meo-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"meo-cli": "index.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
複製代碼
STEP2 : 先寫一個簡單代碼試一試吧~。在根目錄下建立index.js文件,而後狠狠敲入下面的代碼:json
#!/usr/bin/env node
console.log('helo, world!')
複製代碼
而後在命令行執行 meo-cli
, 不出意外的話,你應該看不到輸出hello,world。只會有個報錯提示:數組
zsh: command not found: meo-cli
複製代碼
這是爲什呢?由於咱們並無安裝對應的包,固然就不會連接到全局啦。一個辦法是發包,而後安裝到本地,就能夠了。可是這樣太麻煩,難道每次調試都要發包??bash
有沒有更優雅的方法呢?答案是:有 npm早就想好了對策,就是npm link,它能夠把指定的執行文件連接到全局,使用也很是簡單,在項目根目錄下執行:
npm link
複製代碼
就能夠了。若是你執行命令後,顯示相似安裝npm包的提示,就說明連接成功了。在執行一下 meo-cli
,就能夠看到使人欣慰的hello world了。這裏要注意一下首行的代碼
#!/usr/bin/env node
複製代碼
這段並非註釋,這段代碼是告訴你的腳本工具(bash/zsh), 下面的內容是要在node環境下運行的代碼。千萬不能省略!!!進行下一步以前,讓咱們下來回顧一下vue-cli腳手架是怎麼使用的。
能夠看出來,腳手架會提供一個問答交互的方式,讓使用者輸入或選擇參數,而後根據獲取的參數作出相應的動做(action)。
STEP3: 交互式腳手架的另外一個特色是靈活,使用者能夠根據本身的需求,能夠清晰的去選擇讓腳手架作什麼,不作什麼。要實現這樣的功能,就是要用到開頭提到的插件:
commander是能夠用來獲取命令行的參數,並對參數作響應函數,inquirer則能夠爲咱們提供一個’問答’式的交互體驗。咱們修改一下index.js的代碼:
// index.js
#!/usr/bin/env node
const { program } = require('commander');
program.version('1.0.0')
program.parse(process.argv)
複製代碼
而後咱們在命令行輸入:
meo-cli -V // 1,0,0
複製代碼
就會獲得版本號信息。輸入
meo-cli -h
複製代碼
就會獲得這樣的幫助信息。
這和咱們使用其餘腳手架的體驗是同樣的。
這樣的體驗要歸功於commander.js
commander用法以下:.command(‘init [name]’, ‘init a project’, opts) 功能:註冊一個命令
第一個參數:設置的命令的名稱,後面能夠跟參數,<> 表示必選參數,[]表示可選參數
第二個參數: 命令的描述,可選,注意,當有第二個參數時,不能顯示的調用action做爲命令的回調,須要使用獨立的可執行文件做爲命令
第三個參數:配置參數,如noHelp,isDefault等 .option(’-n, --name | [name]’, ‘desc’, ‘GK’)
功能:定義命令選項,(相似命令的額外參數, 用於輔助命令)
.description(‘this is a command desc’)
功能:命令的描述, 同時會應用到命令的幫助信息中,使用help命令時會顯示
.action(cb):命令的回調函數
.parse() 命令行參數解析, 一般用於最後 e.g.
上面的代碼,定義了一個命令init, 對命令作了描述(description), 並對init命令作了額外參數-t, 表示初始化工程的類型。action是init命令的回調,在回調用打印了參數,即咱們定義一個工程,名字爲demo, 工程類型爲vue。這樣後續工做中就能夠用工程名拼接下載模板到本地的路徑,type=vue 表明咱們會去拉取vue的模板。STEP4: ok, 咱們已經能夠經過自定義命令獲取到參數,接下來就能夠拉取對應模板了。這時咱們就須要使用 download-git-repo
這個包,它能夠用來下載github, gitlab等遠程倉庫的代碼。用法以下:
download(repository, destination, options, callback)
複製代碼
repository: 遠程倉庫的地址。destination:下載到本地的路徑 options: 配置參數 callback: 回調函數 須要注意一下,遠程倉庫地址能夠寫成:
direct: http: //github.com/xxx
複製代碼
即須要完成的倉庫路徑,默認狀況下,會下載master分支,若是要下載其餘分支,在連接後加入分支名,
direct:http://github.com/name/xxx.git#my-branch
複製代碼
小技巧咱們能夠將模板分配到不一樣的分支,而後經過分支來下載不一樣模板。回調函數中會返回下載的結果,根據結果能夠作出不一樣狀態的處理。例如,根據返回狀態判斷是否下載成功,下載成功後提示是否要進行其餘操做(安裝項目依賴,後面會提到哦~)
到此,一個初始化工程的腳手架就完成了。是的,真沒騙人,命令行執行指定命令,獲取參數,拉取模板代碼,就這些~~。
可是, 還能夠作更多。
一般咱們在初始化一個項目時,會提示填寫項目名稱(已經作了),項目描述,做者等信息,這些都是使用者輸入的,也就是命令行的參數,並且會提供一個更友好的「你問我答」 的方式教你作事,不, 求你填寫信息😁😁。這時候就是inquirer上場的時候了,它經過極爲簡單函數方式來提供交互操做。例如,咱們要提示使用者輸入項目描述和做者信息,就能夠這樣寫,在action回調函數中
// ...
inquirer.prompt([
{
name: 'description',
message: '請輸入項目描述'
},
{
name: 'author',
message: '請輸入項目做者',
default: 'robot'
}
])
.then((res) => {})
複製代碼
name表示輸入的鍵名,輸入的值爲value, 即結果會以鍵值對的形式返回。default爲默認值,當直接回車跳過期,會使用默認值。若是但願默認值是空,能夠寫成 default:''
或省略default。
inquirer.prompt() 的參數是一個對象數組,能夠這樣理解,inquirer是流程化的結構,流程能夠是等待輸入,列表選擇,confirm確認yes or no。例如選擇項目模板是,使用者能夠從提供的模板列表中選擇,而不是本身去輸入,就能夠這樣定義參數:
{
name:'type',
type: 'list',
message: 'choose a type of project to init',
choices: ['react', 'vue', 'h5'],
default: 'react',
}
複製代碼
type表示類型,choices爲列表數組,使用者能夠從react,vue,h5中選擇模板,默認會是react模板。
而後根據type的值去拼接git倉庫地址,下載對應模板。
更多inquirer的用法,你們能夠參考 github.com/SBoudrias/I… 來使用,這裏再也不贅述。
STEP5: 咱們已經經過交互方式拿到了項目描述,做者等信息,可是咱們的目的是將這些信息寫入到下載的模板中,也就是package.json中對應的description,author以及項目名稱name中。這要怎麼作呢?這就須要handlebars.js的幫助了,handlebars是一個強大的模板引擎,它能夠解析指定模板,而後根據參數渲染模板。由於咱們要將name, description, author寫入到package.json中,所以咱們要稍微修改一下模板文件:
如圖,將要填寫的字段用{{}} 方式表示,內容就是對應要寫入的變量名字,這和inquirer交互時拿到的字段要保持一致。固然,handlebars不止這麼簡單,更多的用法能夠參考官網 handlebarsjs.com/guide/
這樣,咱們就能夠在模板下載完成後作寫入工做了。
download(url,'./template', { clone: true }, (error) => {
if(!error) {
const packagePath = path.join(downloadPath, 'package.json');
// 判斷是否有package.json, 要把輸入的數據回填到模板中
if (fs.existsSync(packagePath)) {
const content = fs.readFileSync(packagePath).toString();
// handlebars 模板處理引擎
const template = handlebars.compile(content);
const result = template(param);
fs.writeFileSync(packagePath, result);
} else {
console.log('failed! no package.json');
}
}
})
複製代碼
這樣在下載成功後,而且有package.json時纔會去寫入,不然給出錯誤提示。
然而…
咱們發現,到目前爲止,咱們的命令行的輸入一點也很差看,沒有下載中的提示,沒有五彩斑斕醒目的文字… ora 和 chalk這時就起做用了。ora能夠美化命令行的loading,你是轉圈圈,動態的小點點,仍是自定義的gif均可以知足你,chalk就可讓你的命令行文字有了顏色,失敗的紅色告警,成功的綠色提示,都沒問題。
下面是拉取倉庫模板的部分代碼:
const downloadPath = path.join(process.cwd(), name);
const param = {name, ...parameter};
const spinner = ora('正在下載模板, 請稍後...');
spinner.start();
download(
// 直連下載,默認下載master
`direct:http://git.code.oa.com/name/xxx.git#${type}-tpl`,
downloadPath,
{ clone: true },
(error) => {
if (!error) {
// success download
spinner.succeed();
const packagePath = path.join(downloadPath, 'package.json');
// 判斷是否有package.json, 要把輸入的數據回填到模板中
if (fs.existsSync(packagePath)) {
const content = fs.readFileSync(packagePath).toString();
// handlebars 模板處理引擎
const template = handlebars.compile(content);
const result = template(param);
fs.writeFileSync(packagePath, result);
console.log(chalk.green('success! 項目初始化成功!'));
console.log(
chalk.greenBright('開啓項目') + '\n' +
chalk.greenBright('cd ' + name) + '\n' +
chalk.greenBright('start to dvelop~~~!')
)
} else {
spinner.fail();
console.log(chalk.red('failed! no package.json'));
return;
}
} else {
console.log(chalk.red('failed! 拉取模板失敗', error));
return;
}
}
)
複製代碼
到此爲止,一個基礎的工程腳手架就完成了,它能夠經過簡單一條命令,根據輸入的參數,拉取不一樣模板代碼,並將用戶信息回填到模板中,提供了交互式的友好體驗。嗯~,挺香的,收工嘍!!
cd vue-demo
npm run serve
複製代碼
可是… 咱們會發現一個問題,使用vue-cli的時候,在最後會有個運行提示:
它並無提示咱們要npm install, 這說明下載模板的時候項目的依賴也同時安裝。可咱們的沒有這個功能,不行,搞起!!STEP6 : 模板下載好後,咱們要進入模板目錄,而後根據它的package.json安裝依賴,這裏咱們能夠豐富一下,讓使用者在安裝依賴時有選擇:1. 先不安裝依賴,稍後自行安裝, 2. 選擇安裝工具,這時最後的提示要給個npm intall的提示纔算完美。是否安裝依賴。即咱們須要從使用者那裏獲得一個confirm, 根據返回的true或false來決定是否進行下一項安裝。選擇安裝工具。若是使用者選擇安裝,就要提示他選擇安裝工具。這兩個交互一樣可使用inquirer來完成
const continueToInstall = {
type: 'confirm',
name:'next',
message: 'continue to install the project',
default: true,
}
const installTool = {
name: 'tool',
type: 'list',
message: 'choose the tool to install',
choices: ['npm', 'tnpm', 'yarn'],
default: 'npm',
}
複製代碼
這裏作了簡單的封裝:
const { next } = await inquirer.prompt(continueToInstall);
if (next) {
const { tool } = await inquirer.prompt(installTool);
// 安裝項目依賴
const res = await installFunc({ cwd: downloadPath, command: tool });
if (res && res.code === 0) {
processSuccess(name, true, type);
}
} else {
processSuccess(name, false, type);
}
複製代碼
至此,一個簡單的工程腳手架就完成了。
原做者:李澤剛
未經贊成,禁止轉載