前言
大家好,我是小滿,正所謂:工欲善其事,必先利其器!
寫一個開源的項目也不例外,就拿在國内很火的 vue3 架構 和 vite 工具來講,其中的實作與架構設計無不是一個複雜而龐大的工程,而支撐這些工程能順利運作的無不是一個又一個的輪子,正好最近有在閱讀 vue3 和 vite3 的源碼,發現一些較實用的輪子,在這裡分享給大家。
如果你想對前端工程化有所涉獵的話,我相信下面的工具總有一款是你想要的!
1. picocolors
picocolors 是一個可以在終端修改輸出字元樣式的 npm 包,說直白點就是給字元添加顔色;
可能有的同學想到了,這不是跟 chalk 一樣的嗎?
沒錯,他們的作用其實就是一樣的!
為什麼選擇 picocolors:
- 無依賴包;
- 比 chalk 體積小14倍,速度快2倍;
- 支援 CJS 和 ESM 項目;
是以大家明白選什麼了吧!
當然因為 picocolors 包比較小,是以功能邊界沒有 chalk 的全面,但是用在一些自研等絕大部分的需求中是完全可以滿足的。
注意:
因為曆史等原因 vue3 目前還在使用 chalk; vite 已全面用 picocolors 替代作為終端樣式輸出;不過 chalk 為了優化,在最近的最新版本 v5 中已剔除依賴包;
2. prompts vs enquirer vs inquirer
乍一看,可能有的同學會有點懵,其實一句話交代就是:其實他們三都是用來實作指令行互動式界面的工具;
之是以放在一起是因為 vue3 和 vite 所使用的互動式工具不盡相同;
工具名 | 何處使用 | 大小 | 周下載下傳量 | github 位址 |
prompts | vite | 187 kB | 18,185,030 | prompts |
enquirer | vue3 | 197 kB | 13,292,137 | enquirer |
inquirer | 其它 | 87.7 kB | 24,793,335 | inquirer |
npm 近兩年下載下傳熱度趨勢:
簡單總結:
- 其實 vite 起初也是使用的 enquirer,隻是後面為了滿足使用者跨平台使用時出現的 bug,才替換成了 prompts,當然也并不是說 enquirer 不好,隻是場景不同,是以選擇會有所不同罷了;
- 其實如果你想在自己的項目中使用互動式界面工具,我這邊還是比較推薦 inquirer 的,畢竟社群受歡迎程度和功能都是完全滿足你的需求的。
3. cac
cac 是一個用于建構 CLI 應用程式的 JavaScript 庫;
通俗點講,就是給你的 cli 工具增加自定義一些指令,例如 vite create,後面的 create 指令就是通過 cac 來增加的;
因為該庫較适用于一些自定義的工具庫中,是以隻在 vite 中使用, vue3 并不需要該工具;
為什麼不用 commander or yargs?
主要是因為 vite 的工具是針對一些自定義的指令等場景不是特别複雜的情況;
我們看看 cac 的優勢:
- 超輕量級:沒有依賴,體積數倍小于 commander 和 yargs;
- 易于學習:隻需要學習4 API cli.option、cli.version 、cli.help cli.parse 即可搞定大部分需求;
- 功能強大:啟用預設指令,可以像使用 git 的指令一樣友善去使用它,且有參數和選項的校驗、自動生成 help 等完善功能;
當然,如果你想寫一個功能較多的 cli 工具,也是可以選擇 commander 和 yargs 的;
不過一些中小型的 cli 工具我還是比較推薦 cac 的;
4. npm-run-all
npm-run-all 是一個 cli 工具,可以并行、或者按順序執行多個 npm 腳本;npm-run-all 在 vite 工具源碼中有使用;
通俗點講就是為了解決官方的 npm run 指令無法同時運作多個腳本的問題,它可以把諸如 npm run clean && npm run build:css && npm run build:js && npm run build:html 的一長串的指令通過 glob 文法簡化成 npm-run-all clean build:*一行指令。
提供三個指令:
- npm-run-all:可以帶-s 和-p 參數的簡寫,分别對應串行和并行;#依次執行這三個任務指令 npm-run-all clean lint build #同時執行這兩個任務指令 npm-run-all --parallel lint build #先串行執行 a 和 b,再并行執行 c 和 d npm-run-all -s a b -p c d 複制代碼
- run-s:為 npm-run-all --serial的縮寫;
- run-p:為 npm-run-all --parallel的縮寫;
上面隻是簡單的介紹了下,想要了解更多實用功能的,可以去官網檢視;
最後:這個庫屬實是好用,良心推薦!
5. semver
semver 是一個語義化版本号管理的 npm 庫;semver 在 vue3 架構源碼和 vite 工具源碼中都有使用;
說直白一點,你在開發一個開源庫的時候,肯定會遇到要提醒使用者不同版本号不同的情況,那麼如何去判斷使用者版本過低,semver 就可以很好的幫助你解決這個問題;
semver 内置了許多方法,比如判斷一個版本是否合法,判斷版本号命名是否正确,兩個版本誰大誰小之類等等方法;
如下列一些官網的例子:
const semver = require('semver')
semver.valid('1.2.3') // '1.2.3'
semver.valid('a.b.c') // null
semver.clean(' =v1.2.3 ') // '1.2.3'
semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true
semver.gt('1.2.3', '9.8.7') // false
semver.lt('1.2.3', '9.8.7') // true
semver.minVersion('>=1.0.0') // '1.0.0'
semver.valid(semver.coerce('v2')) // '2.0.0'
semver.valid(semver.coerce('42.6.7.9.3-alpha')) // '42.6.7'
複制代碼
6. minimist
minimist 是一個指令行參數解析工具;minimist 在 vue3 架構源碼和 vite 工具源碼中都有使用;
使用:
const args = require('minimist')(process.argv.slice(2))
複制代碼
例如:
# 執行以下指令
vite create app -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
# 将獲得
{ _: [ 'foo', 'bar', 'baz' ],
x: 3,
y: 4,
n: 5,
a: true,
b: true,
c: true,
beep: 'boop' }
複制代碼
特别要說明的是傳回值其中首個 key 是_,它的值是個數組,包含的是所有沒有關聯選項的參數。
如果你的工具在終端有較多的參數,那麼這個工具就非常的适合您!
7. magic-string
magic-string 是一個用于操作字元串和生成源映射的小而快的庫;
其實它最主要的功能就是對一些源代碼和龐大的 AST 字元串做輕量級字元串的替換;
在 vite 工具源碼和 @vue/compiler-sfc 中大量使用;
使用:
import MagicString from 'magic-string';
const s = new MagicString('problems = 99');
// 替換 problems -> answer
s.overwrite(0, 8, 'answer')
s.toString() // 'answer = 99'
// 生成 sourcemap
var map = s.generateMap({
source: 'source.js',
file: 'converted.js.map',
includeContent: true
})
複制代碼
8. fs-extra
fs-extra 是一個強大的檔案操作庫, 是 Nodejs fs 子產品 的增強版;
這個就不多講了,因為它在千錘百煉之下隻能形容它是 YYDS,檢視 更多官方文檔。
9. chokidar
chokidar 是一款專門用于檔案監控的庫;chokidar 隻在 vite 工具源碼中有使用;
其實 Node.js 标準庫中提供 fs.watch 和 fs.watchFile 兩個方法用于處理檔案監控,但是為什麼我們還需要chokidar 呢?
主要是由于 相容性不好、無法監聽、監聽多次 等大量影響性能的問題;
chokidar 用法:
const chokidar = require('chokidar');
const watcher = chokidar.watch('file, dir, glob, or array', {
ignored: /(^|[\/\\])\../,
persistent: true
});
watcher
.on('add', path => console.log(`File ${path} has been added`))
.on('change', path => console.log(`File ${path} has been changed`))
.on('unlink', path => console.log(`File ${path} has been removed`))
.on('addDir', path => console.log(`Directory ${path} has been added`))
.on('unlinkDir', path => console.log(`Directory ${path} has been removed`))
.on('error', error => console.log(`Watcher error: ${error}`))
.on('ready', () => console.log('Initial scan complete. Ready for changes'))
.on('all', (event, path) => console.log(event,path))
.on('raw', (event, path, details) => {
log('Raw event info:', event, path, details);
});
複制代碼
10. fast-glob
fast-glob 是一個快速批量導入、讀取檔案的庫; fast-glob 隻在 vite 工具源碼中有使用;
基本文法:
- * :比對除斜杆、影藏檔案外的所有檔案内容;
- **:比對零個或多個層級的目錄;
- ?:比對除斜杆以外的任何單個字元;
- [seq]:比對 [] 中的任意字元 seq;
如何使用:
const fg = require('fast-glob');
const entries = await fg(['.editorconfig', '**/index.js'], { dot: true });
複制代碼
在 vite 中使用:
vite 工具中 import.meta.glob 方法(如下)就是基于這個庫來實作,是以如果你在自己的工具庫中有批量檔案等的操作,這個庫是以很不錯的選擇;
const modules = import.meta.glob('./dir/*.js', { query: { foo: 'bar', bar: true } })
複制代碼
vite 通過 fast-glob 工具把它生成如下代碼
// vite 生成的代碼
const modules = {
'./dir/foo.js': () =>
import('./dir/foo.js?foo=bar&bar=true').then((m) => m.setup),
'./dir/bar.js': () =>
import('./dir/bar.js?foo=bar&bar=true').then((m) => m.setup)
}
複制代碼
11. debug
debug 是一個模仿 Node.js 核心調試技術的小型 JavaScript 調試程式,在适用于 Node.js 和 Web 浏覽器 都可使用;debug 隻在 vite 工具源碼中有使用;
說直白點就是你可以使用 debug 來對你的程式進行 毫秒級别時間差的統計 對你程式代碼進行優化;
使用:
var debug = require('debug')('http')
, http = require('http')
, name = 'My App';
// fake app
debug('booting %o', name);
http.createServer(function(req, res){
debug(req.method + ' ' + req.url);
res.end('hello\n');
}).listen(3000, function(){
debug('listening');
});
// fake worker of some kind
require('./worker');
複制代碼
如果你對你的代碼或者自研的工具等有較高性能要求,強烈建議可以使用 debug 來進行調式。
12. dotenv
dotenv 是一個零依賴子產品,可将 .env 檔案 中的環境變量加載到 process.env 中;dotenv 隻在 vite 工具源碼中有使用;
如何使用:
- 建立 .env 檔案
- S3_BUCKET="YOURS3BUCKET" SECRET_KEY="YOURSECRETKEYGOESHERE" 複制代碼
- 使用
- import * as dotenv from 'dotenv' dotenv.config() console.log(process.env) 複制代碼
13. esbuild
esbuild 是一個基于 Go 語言開發的 JavaScript 打包工具,被 Vite 用于開發環境的依賴解析;
相比傳統的打包工具,主打性能優勢,在建構速度上可以快 10~100 倍;
到現在知道為啥 vite 為啥快了吧,esbuild 就是第一功臣。
優勢:
- 沒有緩存機制也有極快的打包速度
- 支援es6和cjs子產品
- 支援es6 modules的tree-shaking
- 支援ts和jsx
- sourcemap
- 壓縮工具
- 自定義的插件開發
使用:
esbuild 在 API 層面上非常簡潔, 主要的 API 隻有兩個: Transform 和 Build, 這兩個 API 可以通過 CLI, JavaScript, Go 的方式調用;
- transform:調用這個API能将 ts,jsx 等檔案轉換為js檔案;
// cli
exbuild ./test.ts --loader=ts // 輸出 const str = 'Hello World';
// js api調用
const esbuild = require('esbuild');
const fs = require('fs');
const path = require('path');
const filePath = path.resolve(__dirname, 'test.ts');
const code = esbuild.transformSync(fs.readFilesync(filePath), {
loader: 'ts',
})
console.log(code);
// 輸出
// {
// code: 'const str = 'Hello World'',
// map: '',
// warnings: []
// }
- build:整合了transform後的代碼,可以将一個或者多個檔案轉換并儲存為檔案;
// cli
esbuild test.ts --outfile=./dist/test.js // { errors: [], warnings: [] }
// js api調用
const esbuild = require('esbuild');
const path = require('path');
const result = esbuild.buildSync({
entryPoints: [path.resolve(__dirname, 'test.ts')],
outdir: path.resolve(__dirname, 'dist'),
});
console.log(result); // { errors: [], warnings: [] }
14. rollup
rollup 是一個 JavaScript 子產品打包器,可以将小塊代碼編譯成大塊複雜的代碼,我們熟悉的 vue、react、vuex、vue-router 等都是用 rollup 進行打包的。
在 vite 中的生産環境(Production)就是基于 rollup 打包來建構主要代碼的。
使用:
- 建立 rollup.config.js 檔案
- 配置檔案
export default {
input: 'src/index.js',
output: {
name: 'amapUpper',
file: 'dist/amapUpper.js',
format: 'umd'
},
plugins: []
};
- 運作
{
"scripts": {
"dev": "rollup -i src/index.js -o dist/bundle.js -f es"
},
}
- 執行 npm run dev
15. ws
ws 是一個簡單易用、速度極快且經過全面測試的 WebSocket 用戶端和伺服器實作;完全可以是 Socket.io 的替代方案;ws 隻在 vite 工具源碼中有使用。
說直白一點就是通過 ws,咱們可以實作服務端和用戶端的長連接配接,且通過 ws 對象,就可以擷取到用戶端發送過來的資訊和主動推送資訊給用戶端。
使用:
- server.js
const WebSocket = require('ws')
const WebSocketServer = WebSocket.Server;
// 建立 websocket 伺服器 監聽在 3000 端口
const wss = new WebSocketServer({port: 3000})
// 伺服器被用戶端連接配接
wss.on('connection', (ws) => {
// 通過 ws 對象,就可以擷取到用戶端發送過來的資訊和主動推送資訊給用戶端
var i=0
var int = setInterval(function f() {
ws.send(i++) // 每隔 1 秒給連接配接方報一次數
}, 1000)
})
- client.js
const WebSocket = require('ws')
const ws = new WebSocket('ws://localhost:3000')
// 接受
ws.on('message', (message) => {
console.log(message)
// 當數字達到 10 時,斷開連接配接
if (message == 10) {
ws.send('close');
ws.close()
}
})
16. connect
connect 是一個最早期的 HTTP 伺服器架構,亦可稱為中間件插件;express 就是基于此架構做的擴充;
注意:從 vite2 開始官方已從依賴 koa 轉成 connect 了;
至于為什麼使用 connect 而不是 koa,咱們看官方的回答:
大概意思就是:由于大部分邏輯應該通過插件鈎子而不是中間件來完成,是以對中間件的需求大大減少;是以使用 connect優先級會高于 Koa。
17. esno
esno 是一個基于 esbuild 的 TS/ESNext 的 Node.js 運作時;
說直白點就是可以類似 ts-node 一樣直接運作 TS 檔案,那為甚麼還用 esno 呢?
因為 esno 是基于 esbuild 運作的,esbuild 有多快,上面我們有講到了吧,這裡就不複述了。
使用:
{
"scripts": {
"start": "esno index.ts"
},
"dependencies": {
"esno": "*"
}
}
複制代碼
npm run start
複制代碼
18. tsup
tsup 是一個輕小且無需配置的,由 esbuild 支援的打包工具;
它可以直接把 .ts.tsx 轉成不同格式 esm、cjs、iife 的檔案,快速打包你的工具庫;
使用:
- 安裝 tsup
pnpm i tsup -D
- 在根目錄下的 package.json 中配置
{
"scripts": {
"dev": "pnpm run build -- --watch --ignore-watch examples",
"build": "tsup src/index.ts --dts --format cjs,esm"
},
}
19. vitepress
vitepress 是在 vuepress 的基礎上實作的靜态網站生成器,差別在于 vitepress 是建立在 vite 之上做的開發;
優勢:
- 基于 vite 而不是 webpack,所有更快的啟動時間,熱重載等;
- 使用 vue3 來減少 js 的有效負載;
是以如果你想寫一個線上文檔倉庫,那麼 vitepress 就是一個很好的選擇。
20. vitest
vitest 是一個由 vite 提供支援的快速單元測試架構。
優勢:
- 由 Vite ⚡️提供支援的極速單元測試架構。
- 與 Vite 的配置通用,watch 模式下極快的反應(相當于測試中 HMR)。
- 可以對 Vue/React 元件進行測試。
- 開箱即用 Typescript/JSX/ESM(這一點我想配過 jest 的人應該懂是什麼意思)
- 與 Jest 幾乎相同的 API,同時也有 Jest 的快照功能(這個非常好用!)
- 模拟 DOM
- 生成測試覆寫率
- ESM優先,頂級等待
- 開箱即用的 TypeScript / JSX 支援
- 套件和測試的過濾、逾時、并發
- 等等
是以你還有什麼理由不使用 vitest 呢?
其它
其實細心的同學可能會發現,目前 vue3 和 vite3 都是一個 monorepo 倉庫,且都是使用 pnpm workspace 來進行倉庫管理的;
是以了解 monorepo 和 pnpm workspace 對源碼的閱讀也是有很大的幫助的;
關于這塊更多詳細可以檢視《如何入門 vite 源碼》進行了解。
當然,上面的分享的工具庫隻是在源碼中使用場景較多的庫,還有一些庫由于場景較少 是以這裡沒有做詳細的解釋說明,如果您想了解源碼中的哪個工具庫,歡迎補充,取舍後我會做及時的更新;