相信大家在前端開發中都使用過很多前端腳手架,如vue-cli,create-vite,create-vue等;本篇文章將會為大家詳細介紹這些前端腳手架是如何實現的,並且從零實現一個create-kitty
腳手架釋出到npm上。
使用pnpm管理對專案進行管理是非常方便的,極大的解決了多個包在本地偵錯繁瑣的問題。
什麼是Monorepo?
就是指在一個大的專案倉庫中,管理多個模組/包(package),這種型別的專案大都在專案根目錄下有一個packages資料夾,分多個專案管理。大概結構如下:
-- packages
-- pkg1
--package.json
-- pkg2
--package.json
--package.json
簡單來說就是單倉庫 多專案
目前很多我們熟知的專案都是採用這種模式,如Vant,ElementUI,Vue3等。打造一個Monorepo環境的工具有很多,如:lerna、pnpm、yarn等,這裡我們將使用pnpm來開發我們的UI元件庫。
首先新建資料夾kitty-ui
;然後執行
npm install pnpm -g
pnpm init
shamefully-hoist = true
這裡簡單說下為什麼要設定shamefully-hoist。
如果某些工具僅在根目錄的node_modules時才有效,可以將其設定為true來提升那些不在根目錄的node_modules,就是將你安裝的依賴包的依賴包的依賴包的...都放到同一級別(扁平化)。說白了就是不設定為true有些包就有可能會出問題。
接下就是pnpm如何實現monorepo的了。
為了我們各個專案之間能夠互相參照我們要在根目錄下新建一個pnpm-workspace.yaml檔案將我們的包關聯起來
packages:
- 'packages/**'
這樣就能將我們專案下的packages目錄下的所有包關聯起來了,當然如果你想關聯更多目錄你只需要往裡面新增即可。根據上面的目錄結構很顯然你在根目錄下新packages資料夾,packages資料夾新建create-kitty
以及測試用的cli-demo
資料夾,並分別執行pnpm init
。
在create-kitty
的package.json
中新增bin
屬性然後指向index.js
。這裡去掉了main
屬性,因為我們開發的不是工具包。
{
"name": "create-kitty",
"version": "1.0.0",
"description": "",
"bin": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
當然,bin也支援物件格式,讓腳手架可以使用多個命令
"bin": {
"create-kitty": "index.js",
"create-app": "index.js"
},
新建create-kitty/index.js作為腳手架入口
#! /usr/bin/env node
console.log("hello kitty")
注意cli入口檔案需要加#! /usr/bin/env node
指定指令碼的解釋程式為node,否則會報錯
來到測試包cli-demo
資料夾下直安裝create-kitty
pnpm add create-kitty
然後就會發現cli-demo
下的package
出現依賴,同時加上scripts
{
"name": "cli-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"create-kitty": "create-kitty"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"create-kitty": "workspace:^1.0.0"
}
}
然後執行pnpm run create-kitty
說明
當我們上線的時候可以直接執行npm create kitty
或者npm init kitty
,它們其實就是執行npx create-kitty
一般腳手架都會根據使用者輸入不同命令來做出不同操作,比如使用者輸入kitty-ui -v
提示版本號,kitty-ui -h
顯示幫助資訊等。而實現這些基本原理就是process.argv
將create-kitty
下的入口檔案index.js
修改為
#! /usr/bin/env node
console.log(process.argv);
然後執行這個檔案並帶幾個引數
然後會發現使用者傳的引數在process.argv
陣列的第二位以後,這樣我們就可以根據使用者輸入不同引數做不同操作了。但是這樣很不方便,所以我們可以使用框架來解決。
首先安裝command-line-args
庫
pnpm add command-line-args -S
接下來看它是如何獲取使用者輸入引數的
#! /usr/bin/env node
import commandLineArgs from 'command-line-args';
//設定命令引數
const optionDefinitions = [
{ name: 'version', alias: 'v', type: Boolean },
{ name: 'arg1', type: String },
{ name: 'arg2', type: Number },
];
const options = commandLineArgs(optionDefinitions);
console.log(options);
注意這裡使用了es6的語法,所以需要將package.json
中的type指定為module
接下來實戰一些,檢測使用者輸入--help
指令,然後我們輸出一個幫助的設定。
首先安裝command-line-usage
pnpm add command-line-usage -S
首先引入commandLineUsage
,然後寫個設定
import commandLineUsage from 'command-line-usage';
//幫助內容
const helpSections = [
{
header: 'create-kitty',
content: '一個快速生成元件庫搭建環境的腳手架',
},
{
header: 'Options',
optionList: [
{
name: 'version',
typeLabel: '{underline boolean}',
description: '版本號',
},
{
name: 'arg1',
typeLabel: '{underline string}',
description: '引數1',
},
{
name: 'arg2',
typeLabel: '{underline number}',
description: '引數2',
},
],
},
];
在command-line-args
中設定個help
命令,當獲取到help
的時候直接列印
const optionDefinitions = [
{ name: 'help', alias: 'h', type: Boolean },
{ name: 'version', alias: 'v', type: Boolean },
{ name: 'arg1', type: String },
{ name: 'arg2', type: Number },
];
const options = commandLineArgs(optionDefinitions);
if (options.help) console.log(commandLineUsage(helpSections))
但是這樣好像不太好看