秒懂vue3+vite3原始碼,只要會這20個庫!

2022-07-28 18:01:44
正所謂:工欲善其事,必先利其器!寫一個開源的專案也不例外,就拿在國內很火的 vue3 框架vite 工具 來講,其中的實現與架構設計無不是一個 複雜而龐大的工程,而支撐這些工程能順利執行的無不是一個又一個的輪子,正好最近有在閱讀 vue3 和 vite3 的原始碼,發現一些較實用的輪子,在這裡分享給大家。

如果你想對 前端工程化 有所涉獵的話,我相信下面的工具總有一款是你想要的!

1. picocolors

picocolors 是一個可以在終端修改輸出字元樣式的 npm 包,說直白點就是給字元新增顏色;

1.png

可能有的同學想到了,這不是跟 chalk 一樣的嗎?

沒錯,他們的作用其實就是一樣的!

為什麼選擇 picocolors:

  • 無依賴包;
  • 比 chalk 體積小 14 倍,速度快 2 倍;
  • 支援 CJS 和 ESM 專案;

所以大家明白選什麼了吧!

當然因為 picocolors 包比較小,所以功能邊界沒有 chalk 的全面,但是用在一些自研等絕大部分的需求中是完全可以滿足的。

注意:

  • 因為歷史等原因 vue3 目前還在使用 chalk

  • vite 已全面用 picocolors 替代作為終端樣式輸出;

  • 不過 chalk 為了優化,在最近的最新版本 v5 中已剔除依賴包;

2. prompts vs enquirer vs inquirer

乍一看,可能有的同學會有點懵,其實一句話交代就是:其實他們三都是用來 實現命令列互動式介面 的工具;

之所以放在一起是因為 vue3vite 所使用的互動式工具不盡相同;

2.gif

工具名何處使用大小周下載量github 地址
promptsvite187 kB18,185,030prompts
enquirervue3197 kB13,292,137enquirer
inquirer其它87.7 kB24,793,335inquirer

npm 近兩年下載熱度趨勢:

3.png

簡單總結:

  • 其實 vite 起初也是使用的 enquirer,只是後面為了滿足使用者跨平臺使用時出現的 bug,才替換成了 prompts,當然也並不是說 enquirer 不好,只是場景不同,所以選擇會有所不同罷了;

  • 其實如果你想在自己的專案中使用 互動式介面 工具,我這邊還是比較推薦 inquirer 的,畢竟社群受歡迎程度和功能都是完全滿足你的需求的。

3. cac

cac 是一個用於構建 CLI 應用程式的 JavaScript 庫;

通俗點講,就是給你的 cli 工具增加自定義一些命令,例如 vite create,後面的 create 命令就是通過 cac 來增加的;

因為該庫較適用於一些自定義的工具庫中,所以只在 vite 中使用, vue3 並不需要該工具;

為什麼不用 commander or yargs

主要是因為 vite 的工具是針對一些自定義的命令等場景不是特別複雜的情況;

我們看看 cac 的優勢

  • 超輕量級:沒有依賴,體積數倍小於 commanderyargs
  • 易於學習:只需要學習 4 API cli.optioncli.versioncli.help cli.parse 即可搞定大部分需求;
  • 功能強大:啟用預設命令,可以像使用 git 的命令一樣方便去使用它,且有引數和選項的校驗、自動生成 help 等完善功能;

當然,如果你想寫一個功能較多的 cli 工具,也是可以選擇 commanderyargs 的;

不過一些中小型的 cli 工具我還是比較推薦 cac 的;

4. npm-run-all

npm-run-all 是一個 cli 工具,可以並行、或者按順序執行多個 npm 指令碼;npm-run-allvite 工具原始碼中有使用;

通俗點講就是為了解決官方的 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 庫;semvervue3 框架原始碼和 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 是一個命令列引數解析工具;minimistvue3 框架原始碼和 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.watchfs.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.jsWeb 瀏覽器 都可使用;debug 只在 vite 工具原始碼中有使用;

說直白點就是你可以使用 debug 來對你的程式進行 毫秒級別時間差的統計 對你程式程式碼進行優化;

4.png

使用:

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 倍;

5.png

到現在知道為啥 vite 為啥快了吧,esbuild 就是第一功臣。

優勢:

  • 沒有快取機制也有極快的打包速度
  • 支援es6和cjs模組
  • 支援es6 modules的tree-shaking
  • 支援ts和jsx
  • sourcemap
  • 壓縮工具
  • 自定義的外掛開發

使用:

esbuild 在 API 層面上非常簡潔, 主要的 API 只有兩個: TransformBuild, 這兩個 API 可以通過 CLI, JavaScript, Go 的方式呼叫;

  • transform:呼叫這個API能將 tsjsx 等檔案轉換為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 打包來構建主要程式碼的。

使用:

1、建立 rollup.config.js 檔案

2、組態檔

export default {
  input: 'src/index.js',
  output: {
    name: 'amapUpper',
    file: 'dist/amapUpper.js',
    format: 'umd'
  },
  plugins: []
};

3、執行

{
  "scripts": {
    "dev": "rollup -i src/index.js -o dist/bundle.js -f es"
  },
}

4、執行 npm run dev

15. ws

ws 是一個簡單易用、速度極快且經過全面測試的 WebSocket 使用者端伺服器 實現;完全可以是 Socket.io 的替代方案;ws 只在 vite 工具原始碼中有使用。

說直白一點就是通過 ws,咱們可以實現伺服器端和使用者端的長連線,且通過 ws 物件,就可以獲取到 使用者端傳送過來的資訊主動推播資訊給使用者端

使用:

1、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) 
    })

2、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,咱們看官方的回答:

6.png

大概意思就是:由於大部分邏輯應該通過外掛勾點而不是中介軟體來完成,因此對中介軟體的需求大大減少;所以使用 connect優先順序會高於 Koa

17. esno

esno 是一個基於 esbuildTS/ESNextNode.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 的檔案,快速打包你的工具庫;

使用:

1、安裝 tsup

pnpm i tsup -D

2、在根目錄下的 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 呢?

其它

其實細心的同學可能會發現,目前 vue3vite3 都是一個 monorepo 倉庫,且都是使用 pnpm workspace 來進行倉庫管理的;

所以瞭解 monorepopnpm workspace 對原始碼的閱讀也是有很大的幫助的;

關於這塊更多詳細可以檢視《如何入門 vite 原始碼》進行了解。

https://juejin.cn/post/7094984070999834655

當然,上面的分享的工具庫只是在原始碼中使用 場景較多的庫,還有一些庫由於 場景較少 所以這裡沒有做詳細的解釋說明,如果您想了解原始碼中的哪個工具庫,歡迎補充,取捨後我會做及時的更新;