目錄
- 腳手架
- 全局命令
- 交互式輸入
- 拉取遠程倉庫代碼
- 優化
- 總結
1、腳手架
今天工地的磚有點燙,我抬頭望了望天,思考了半分鍾,決定為了以后搬磚方便,先搭個腳手架:
不好意思,拿錯了,是這個:
相信很多小伙伴都用過vue-cli、create-react-app或angular-cli之類的腳手架,一個命令行就能快速搭起項目框架,告別刀耕火種的復制粘貼,分分鍾解放生產力。
作為一個有追求的切圖仔,這種摸魚神器,必須立馬安排。
好了,那么問題來了:什么是腳手架?
從表現形式上來看,腳手架主要有以下幾個特點:
- 一個能全局執行的命令;
- 能實現交互式輸入,比如輸入項目名稱,選擇配置項等;
- 能自動拉取github或gitlab遠程倉庫的代碼。
當然高級的腳手架肯定不止這么點功能,不過我們先從最簡單的實現起。
2、全局命令
2.1 基本原理
先拆包觀察一下,類似vue-cli
這類的全局命令是如何實現的。
首先找到vue-cli
的安裝路徑,全局安裝可以通過npm config get prefix
找到路徑,比如我的windows系統就是
C:\Users\用戶名\AppData\Roaming\npm:
可以看到這個目錄下有很多與全局命令同名的文件,以及一個node_modules
文件夾。我們全局安裝的依賴包就在node_modules
里。找到@vue\cli
,這個就是vue-cli
的源碼包。
vue-cli
源碼包中的package.json
有這樣一個關鍵配置:
"bin": {
"vue": "bin/vue.js"
},
這個bin/vue.js
腳本必須以#!/usr/bin/env node
開頭,
當npm全局安裝時,就會根據這個配置,生成對應的同名可執行文件。
而當我們運行vue
時,系統就會以node程序來運行vue.js。
npm官方文檔中提供了npm link
命令,用以讓用戶將自定義的腳手架生成一個全局命令。
npm link
主要做了兩步操作:
- 一是在npm全局安裝路徑下的node_modules文件夾里生成一個鏈接文件,這個鏈接文件指向執行該命令的文件夾,也就是我們的腳手架源碼文件夾;
- 二是在npm全局安裝路徑下生成與配置bin里同名的可執行文件。
所以,通過npm link
,我們就可以生成一個node全局命令。
npm link
官方文檔:https://docs.npmjs.com/cli/v6/commands/npm-link。
2.2 全局命令的實現
話不多說,先來擼一個全局命令。
1)執行npm init
初始化一個package.json
:
2)在package.json
里加上bin
配置:
"bin": {
"test-cli": "./test.js"
},
3)添加對應的執行腳本文件test.js
:
#! /usr/bin/env node
console.log("Hello! My CLI!");
4)在package.json
同級目錄下執行npm link
5)在控制台運行一下我們定義的全局命令test-cli
,看到輸出結果:
完成!
3、交互式輸入
通過上述操作,用戶已經可以通過全局命令執行到我們的test.js
文件,剩下的功能我們就可以在js里去自由發揮了。
Node社區有着數量龐大的第三方模塊,藉由這些模塊我們可以快速開發實現想要的功能。
inquirier
就是其中之一,目前在github上有14.5k的star,它的目標就是“致力於成為一個易於嵌入且美觀的命令行工具”。顧名思義,通過inquirier
模塊,我們可以在命令行中實現與用戶的輸入交互。
我們需要知道用戶要創建的項目名稱是什么,以及用戶想要下載哪個遠程倉庫的代碼:
const inquirer = require("inquirer");
inquirer
.prompt([
{
type: "input",
name: "project",
message: "項目名稱",
},
{
type: "list",
name: "tpl",
message: "請選擇模板",
choices: ["vue", "react"],
}
])
.then((res) => {
console.log(res);
const { project, tpl } = res;
// project就是用戶輸入的項目名稱
// tpl就是用戶選擇的模板
});
這段代碼就是為用戶提供了一個input輸入選項,來輸入項目名稱,以及一個list列表選項,來選擇要下載的模板(之后我們再根據這個模板名稱去對應的倉庫地址進行下載)。
inquirer
還有更多的選項功能,感興趣的小伙伴可以去官方文檔上自由探索:https://github.com/SBoudrias/Inquirer.js。
4、拉取遠程倉庫代碼
現在我們已經知道用戶要下載的是哪個模板代碼,也知道這些模板代碼對應的下載地址:
const stores = [
{
name: "vue",
url: "https://github.com/vuejs/vue.git"
},
{
name: "react",
url: "https://github.com/facebook/react.git"
}
]
拉取github或者gitlab遠程倉庫代碼的第三方模塊也有好幾個,我選用的是nodegit
,這個項目在github上目前有4.9k的star,用起來也很簡單:
const Git = require("nodegit");
/** 克隆遠程倉庫代碼 */
// url: 源碼倉庫地址; path: 要下載的目標路徑; cb: 下載結束后的回調函數
const gitClone = (url, path, cb)=>{
console.log("正在下載遠程倉庫代碼...")
console.log(url)
Git.Clone(url, path)
.then(function(res) {
console.log("下載完成")
cb(true)
})
.catch(function(err) { console.log("下載失敗"+err);cb(false) });
}
nodegit官網地址:
https://github.com/nodegit/nodegit
至此三個基本功能都已實現!
5、優化
深諳摸魚之道的我想了想,覺得還能更進一步,比如下載完源碼,再幫我自動安裝下依賴包啦:
const process = require('child_process');
/** 安裝依賴包 */
const install = (path)=>{ // path是源碼模板中package.json所在的路徑
console.log("正在安裝依賴包...")
const cmd = 'cd '+path+' && yarn';
process.exec(cmd, function(error, stdout, stderr) {
console.log(error);
console.log(stdout);
console.log(stderr);
console.log("安裝完成")
});
}
之前看到很多腳手架出場都自帶拉風的藝術字,作為一個有追求的切圖仔,這個必須安排!
這個也是用到了一個第三方模塊figlet
(https://github.com/patorjk/figlet.js):
const figlet = require('figlet');
figlet('My CLI!', {horizontalLayout:"full"}, function(err, data) {
if (err) {
console.log('Something went wrong...');
console.dir(err);
return;
}
console.log(data)
// do something...
});
很多腳手架還提供了參數配置、幫助信息等功能,這個大多是通過commander
模塊實現的(https://github.com/tj/commander.js,20.7k star),這里就不詳細展開了,我在demo項目里也簡單實現了下,確實很強大很好用。
Demo已開源:https://github.com/youzouzou/testcli。
這個demo只是簡單地實現了腳手架的基本功能,探索了一下幾個node模塊的用法,還有很多需要優化的點。進一步學習的最好辦法就是去看那些成熟的腳手架源碼,然后去模仿去實踐,再結合實際情況,摸索出最適合自己團隊的方案。
6、總結
工欲善其事,必先利其器。
實際上腳手架並不拘泥於上面的實現形式,凡是能提高效率的工具,在某種意義上都可以稱之為腳手架。
模仿只是最初級的階段,創新才是真正的開始。