準(zhǔn)備工作
在開始搭建前,確保本機安裝了node,為避免奇奇怪怪的問題 建議node版本16以上
搭建腳手架
使用過vue ,react,angular的同學(xué)都知道 ,應(yīng)該對腳手架有一定的理解,比如vue-cli的 vue create myApp ,其中vue 就是vue-cli聲明的一個命令,下來我們創(chuàng)建一個項目并聲明自己的命令。
創(chuàng)建項目
創(chuàng)建一個空的文件夾 demo,并在demo下初始化npm, 其中 -y 表示都采用默認(rèn)值,執(zhí)行成功后會在該目錄下 生成package.json文件
npm init -y
聲明命令
1.在package.json中 添加 “bin” 字段,來聲明命令,其中 create-demo
就是我們本次聲明的命令,./bin/index.js
是我們運行 create
后執(zhí)行的文件路徑。
"bin" :{
"create-demo" : "./bin/index.js"
}
- 創(chuàng)建 bin/index.js , 在根目錄下 創(chuàng)建bin 目錄 ,并且創(chuàng)建index.js, 創(chuàng)建后的內(nèi)容如下
#!/usr/bin/env node
console.log('簡易腳手架demo')
#!/usr/bin/env node 其中
#!
代表標(biāo)注文件可以當(dāng)做腳本運行/usr/bin/env
代表絕對地址node
代表需要使用的執(zhí)行器類型
搭建本地調(diào)式環(huán)境
創(chuàng)建完成后,我們不能直接執(zhí)行create-demo
命令,需要搭建一個本地調(diào)式的環(huán)境,這部分最簡單的就是 npm link
, 這個命令就是將本地的項目鏈接到全局,等同于 npm i -g xxx
.
npm link
執(zhí)行后如下
npm link
后不生效問題
這個問題是一般都是因為環(huán)境變量沒有配置正確導(dǎo)致的 ,我們先檢查下 當(dāng)前npm 的配置npm config list --global
找到prefix
的配置路徑,將其添加在本機的 環(huán)境變量中
這時候代表我們已經(jīng)初始化成功,這個時候再去調(diào)用 create-demo
,這個時候就可以看到之前在 bin 目錄下打印的內(nèi)容
校驗node版本
剛開始我們說了,為避免不必要的麻煩,盡量要求用戶nodejs版本16以上,這里我們可以用 semver
這個來做個校驗。
創(chuàng)建util文件夾,這里文件夾主要放我們封裝的一些工具函數(shù), 創(chuàng)建 index.js
const semver = require('semver');
/**
* 檢測nodejs版本 是否大于16
*/
function checkNodeVersion() {
const unSupportedVer = semver.lt(process.version, 'v16.0.0');
console.log(`當(dāng)前node版本${process.version}`);
if(unSupportedVer){
throw new Error('Node.js 版本過低,推薦升級 Node.js 至 v16.0.0+')
}
}
module.exports = {
checkNodeVersion
}
然后在 /bin/index.js
中添加
const { checkNodeVersion } = require('../util/index');
console.log('簡易腳手架demo');
checkNodeVersion();
到這里我們一個簡易的腳手架就搭建成功了,當(dāng)然一個腳手架不止這些內(nèi)容,比如與用戶的交互,下載依賴等等,下面將一一講述。
與用戶的交互
命令參數(shù)的獲取
Node.js 中的 process 模塊提供了當(dāng)前 Node.js 進程相關(guān)的全局環(huán)境信息。其中process.argv
表示用戶輸入的 命令行信息,我們可以簡單打印下。
console.log(process.argv)
打印結(jié)果如下
可以看到 其中 myApp 就是本次輸入的內(nèi)容,當(dāng)然參數(shù)還有多種情況輸入 比如下面這兩種
create-demo --name=myApp
create-demo --name myApp
這個時候我們打印出來的信息也不同,意味這我們需要處理參數(shù)的多種情況,這里推薦一個插件minimist
,來處理。
npm install minimist
通過上面的打印我們注意到,用戶輸入的從數(shù)組的第二個開始,這里我們處理下
#!/usr/bin/env node
const minimist = require('minimist');
const { checkNodeVersion } = require('../util/index');
console.log('簡易腳手架demo');
checkNodeVersion();
console.log(minimist(process.argv.slice(2)))
使用結(jié)果如下,這樣我們就處理了命令參數(shù)這塊。
這里添加幾個簡單的命令 --h,--v
對應(yīng)help和version,以及init,添加后的代碼如下
在 bin/index.js 中
#!/usr/bin/env node
const minimist = require('minimist');
const { checkNodeVersion , getPkgVersion } = require('../util/index');
function run (){
checkNodeVersion();
parseArgs();
}
// 處理用戶命令行輸入信息
function parseArgs(){
const args = minimist(process.argv.slice(2),{
alias :{
version: "v",
help: ["h"],
}
});
// 這里指的是 用戶輸入的第二個命令 比如 vue create-app 中的 create-app
const command = args._[0];
if(command){
switch (command) {
case 'init':
console.log('創(chuàng)建簡易腳手架demo');
break;
default:
break;
}
} else {
if (args.h) {
console.log('Usage: create-demo <command> [options]')
console.log()
console.log('Options:')
console.log(' -v, --version 查看當(dāng)前版本')
console.log(' -h, --help 查看幫助')
console.log()
console.log('Commands:')
console.log(' init 創(chuàng)建一個新的項目')
} else if (args.v) {
console.log(getPkgVersion());
}
}
}
run();
在 util/index.js 中
const semver = require("semver");
const path = require('path');
/**
* 檢測nodejs版本 是否大于16
*/
function checkNodeVersion() {
const unSupportedVer = semver.lt(process.version, "v16.0.0");
console.log(`當(dāng)前node版本${process.version}`);
if (unSupportedVer) {
throw new Error("Node.js 版本過低,推薦升級 Node.js 至 v16.0.0+");
}
}
/**
* 獲取根目錄
*/
function getRootPath() {
return path.resolve(__dirname, "../");
}
/**
* 查看當(dāng)前版本信息
*/
function getPkgVersion(){
return require(path.join(getRootPath(), 'package.json')).version
}
module.exports = {
checkNodeVersion,
getPkgVersion
}
來看下 輸出結(jié)果
此時整個目錄結(jié)構(gòu)如下
- demo
- bin
- index.js
- util
- index.js
- package.json
- bin
詢問式交互
一個好的腳手架,與用戶的交互是少不了的,這里我們采用詢問式交互, 一問一答的來收集用戶選擇的信息。比如npm init 通過詢問式的交互來完成package.json 內(nèi)容。
這里推薦使用 inquirer
庫來完成,這個相對來說功能比較齊全,比如對值的一些校驗,過濾不必要的值等等。
npm i inquirer
我們這里 主要是通過 inquirer.prompt
來完成,prompt接收一個數(shù)組,其中每一項都是一個問題對象,每一項下面簡單描述下問題對象參數(shù)的含義
參數(shù)名稱 | 值的含義 | 類型 |
---|---|---|
type | 問題的類型 |
input | confirm | list | checkbox
|
name | 問題答案的變量 | string |
message | 問題描述 | string |
default | 默認(rèn)值 | any |
choices | 列表選項 | any[] |
validate | 對當(dāng)前值的校驗 | Function |
filter | 過濾所選值 | Fucnton |
下來我們直接在剛剛的init里面,添加讓用戶輸入項目名稱的問題。
case 'init':
console.log('創(chuàng)建簡易腳手架demo');
inquirer.prompt([{
type: "input",
name: "projectName",
message: "請輸入項目名稱!",
}])
break;
這個時候我們再去終端查看
當(dāng)然一般創(chuàng)建項目肯定不止一個問題 ,下面我們完善下代碼
把 問題部分抽離出成一個ask.js, 并添加其余情況的處理,該文件位于 bin 目錄下bin/ask.js
const inquirer = require("inquirer");
const fs = require("fs");
async function init(cb) {
try {
const answer = await ask();
console.log(answer);
cb(answer);
} catch (err) {
console.log(chalk.red("項目創(chuàng)建失敗:", err));
}
}
/**
* 詢問式收集信息
* @returns 用戶的答案信息
*/
async function ask() {
let prompts = [];
let ans = {};
// 每一個問題一個函數(shù)
askProjectName(ans, prompts);
const answer = await inquirer.prompt(prompts);
return answer;
}
/**
* 創(chuàng)建的項目名稱
* @param {*} ans 答案信息
* @param {*} prompts 問題對象
*/
function askProjectName(ans, prompts) {
if (typeof ans.projectName !== "string") {
prompts.push({
type: "input",
name: "projectName",
message: "請輸入項目名稱!",
validate(input) {
if (!input) {
return "項目名不能為空!";
}
if (fs.existsSync(input)) {
return "當(dāng)前目錄已經(jīng)存在同名項目,請換一個項目名!";
}
return true;
},
});
} else if (fs.existsSync(ans.projectName)) {
prompts.push({
type: "input",
name: "projectName",
message: "當(dāng)前目錄已經(jīng)存在同名項目,請換一個項目名!",
validate(input) {
if (!input) {
return "項目名不能為空!";
}
if (fs.existsSync(input)) {
return "項目名依然重復(fù)!";
}
return true;
},
});
}
}
module.exports = {
askInit
};
同時創(chuàng)建init.js,預(yù)留后續(xù)會有新的命令出現(xiàn),這里把之前校驗node版本的也放進來
bin/init.js
const { askInit } = require('./ask');
const { checkNodeVersion } = require('../util/index');
// 執(zhí)行 init 命令后的處理
async function init(){
checkNodeVersion();
await askInit();
}
module.exports = {
init
}
在 bin/index.js
中,引入init 在用戶輸入init
后執(zhí)行
///...
const { init } = require('./init');
///...
case 'init':
init();
break;
弄完我們試驗下
到這里我們與用戶交互大致就完成了。下來我們來創(chuàng)建項目。
創(chuàng)建項目
創(chuàng)建項目這里大部分都是復(fù)制文件去生成,但是因為我們搭建的是一個簡易的腳手架,沒有要生成的目錄,所以這里我們直接去git上去 克隆一個項目來生成自己的項目。
這里我們直接 在終端輸入 git clone 你的項目地址
來完成克隆。這里 我們先用 child_process
中的 exec 來實現(xiàn)
child_process 為node 內(nèi)置的模塊 ,無需額外下載 。 具體使用參考官網(wǎng) https://nodejs.org/api/child_process.html
bin/init.js
const {askInit} = require("./ask");
const {checkNodeVersion} = require("../util/index");
const {exec} = require("child_process");
const chalk = require('chalk');
// 執(zhí)行 init 命令后的處理
async function init() {
checkNodeVersion();
await askInit(createProject);
}
/**
* 創(chuàng)建項目
*/
function createProject(answer) {
const {projectName} = answer;
exec(`git clone 你的項目地址 ${projectName}`, (err) => {
if (err) {
console.log(chalk.red(err));
} else {
console.log(chalk.green("項目創(chuàng)建成功,即將安裝依賴"));
// 安裝依賴
}
});
}
module.exports = {
init,
};
此時我們?nèi)ヒ粋€新的目錄打開終端,并輸入 create-demo init
,結(jié)果如下
這個時候項目已經(jīng)創(chuàng)建成功了。下面將安裝依賴
chalk
: 命令行輸出美化工具 這里我使用的是 3.0.0版本的
安裝依賴
基于克隆項目,安裝依賴這里就相對比較簡單了,也是直接去掉終端。
/**
* 創(chuàng)建項目
*/
function createProject(answer) {
const {projectName} = answer;
exec(`git clone git@gitee.com:leadersir/resume.git ${projectName}`, (err) => {
if (err) {
console.log(chalk.red(err));
} else {
console.log(chalk.green("項目創(chuàng)建成功,即將安裝依賴"));
// 安裝依賴
const command = `cd ${process.cwd()}/${projectName} && yarn`;
install(command);
}
});
}
/**
* 下載依賴
*/
function install(command) {
const child = exec(command,(err)=>{
if(err){
console.log(chalk.red('安裝項目依賴失敗,請自行重新安裝!'))
} else {
console.log(chalk.gray('安裝成功'))
}
});
// 輸出安裝的信息
child.stdout.on('data',(data)=>{
console.log(data);
})
// 輸出安裝的信息
chalk.stderr.on('data',(data)=>{
console.log(data);
})
}
此時我們運行,就可以正常安裝依賴了。當(dāng)然安裝依賴這里我們可以加個loading,讓用戶更直觀的感受到,這里推薦使用開源庫ora來實現(xiàn)加載動畫。
npm i ora@5.0.0
優(yōu)化后代碼如下
function install(command) {
const installSpinner = ora(`正在安裝依賴, 請耐心等待...`).start()
const child = exec(command,(err)=>{
if(err){
installSpinner.fail(chalk.red('安裝項目依賴失敗,請自行重新安裝!'))
} else {
installSpinner.succeed(chalk.gray('安裝成功'));
}
});
child.stdout.on('data',(data)=>{
installSpinner.stop()
console.log(data.replace(/\n$/, ''))
installSpinner.start()
})
child.stderr.on('data',(data)=>{
console.log(data.replace(/\n$/, ''))
installSpinner.start()
})
}
最后我們再給用戶一個提示 說明項目已經(jīng)創(chuàng)建成功了 ,可以開發(fā)了。
/**
* 創(chuàng)建項目成功回調(diào)
*/
function callSuccess() {
console.log(chalk.green(`創(chuàng)建項目成功!`));
console.log(chalk.green(` 開始工作吧!??`));
}
添加到依賴下載成功這里
installSpinner.succeed(chalk.gray("安裝成功"));
callSuccess();
此時執(zhí)行完成后 結(jié)果如下
到這里我們的腳手架就搭建成功了,最后我們發(fā)布到github 上
npm publish
項目目錄&依賴版本信息
文章來源:http://www.zghlxwxcb.cn/news/detail-828339.html
常用依賴包
-
chalk
: 命令行美化工具 -
inquirer
: 命令行交互工具 -
minimist
: 命令行處理工具 -
ora
: 命令行加載美化工具 -
semver
: 版本比較工具 -
request
: 處理請求
結(jié)語
上面主要是講述如何搭建一個腳手架,比較簡單,但這些方法都比較常用,是搭建腳手架的基本功,希望對你有所幫助。以上代碼已上傳至 gitee中 ,大家可以下載實踐。文章來源地址http://www.zghlxwxcb.cn/news/detail-828339.html
到了這里,關(guān)于前端如何搭建腳手架并在本地運行的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!