元件庫是一組可重複使用的元件,可以用於多個專案。它可以幫助不同專案和團隊共享資源,這些元件可以是低階通用元件(例如按鈕、輸入框和模態框),也可以是特定業務模組。簡而言之,元件庫是跨專案共享程式碼的有效方法,能夠顯著節省時間。
然而,使用 Vue.js 構建元件庫並不像看起來那樣容易,因為 Vue.js 的單檔案元件 (SFC) 帶來了挑戰。在標準 TypeScript 專案中,通常使用 tsup 或 Vite 將程式碼轉譯為 JavaScript,並進行打包。
關鍵原則: 儘可能以原生方式交付包程式碼,讓使用者工具負責轉譯和最佳化。這是構建庫時應牢記的原則,能極大地簡化流程。
Vue.js SFC 的挑戰:
讓我們以一個使用可組合函式的元件為例:
<script setup> import { useUser } from '../composables' const { user } = useUser() </script> <template> <div v-if="user"> {{ user.name }} </div> </template>
可組合函式程式碼如下:
import { ref } from 'vue' export function useUser() { const user = ref({ name: 'John Doe' }) return { user } }
這個元件使用了另一個檔案中的可組合函式,這看似簡單,但它帶來了一個顯著的挑戰。
專案的結構如下:
src/ components/ User.vue composables/ useUser.ts index.ts
index.ts
是庫的入口檔案,內容如下:
import User from './components/User.vue' export { User } export { useUser } from './composables/useUser'
package.json
包含一些 exports
配置:
{ "type": "module", "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs" } }, "main": "dist/index.mjs", "types": "dist/index.d.ts" }
類似標準 TypeScript 專案
注意: 包是一個包含所有專案程式碼的單個檔案。它透過減少需要載入的檔案數量來最佳化專案的載入速度。在建立 npm 包時,這個包不會進行轉譯(除了 TypeScript 到 JavaScript)或壓縮。
如果嘗試打包這個專案,dist
資料夾的結構如下:
dist/ index.cjs
檢查 index.cjs
檔案後,你會發現可組合函式 useUser
被內聯了,但元件卻被完全忽略了。如果打包工具執行成功,可能會出現類似 No loader is configured for ".vue" files
的錯誤。這意味著像 esbuild 或 tsc 這樣的工具不知道如何處理 Vue 檔案,這是正常的,因為 Vue 檔案不是 JavaScript 檔案。
問題很明顯:我們需要一些配置來處理 Vue 檔案。在網上搜索 "vue loader",因為我們使用了 Vue 檔案,需要將它們轉譯為 JavaScript。
搜索結果中最頂端的是 Vue Loader,它是一個 Webpack 載入器。但是,我不打算使用 Webpack,因為 Vite 已經成為行業標準,在這種情況下沒有理由使用 Webpack。
不過,在我們深入之前,讓我們回顧一下我們的口號:儘可能以原生方式交付包程式碼,並遵循 KISS 原則。 使用 Webpack 來轉譯 Vue SFC 檔案似乎違反了這個原則。
忽略 Vue 檔案
爲了不轉譯 .vue
檔案,我們可以指示打包工具忽略它們。這可以使用 UnJS 生態系統中的 unbuild 工具來實現。
Unbuild 是一個簡單但高度可配置的工具,因為它構建在 rollup 之上。
對於我們的小專案,我們可以直接進行嘗試:
npx unbuild
不幸的是,它會因為與之前相同的原因而失敗:
src/components/ShowGitHubUser.vue (1:0): Expression expected (Note that you need plugins to import files that are not JavaScript)
但 unbuild 的潛力遠不止此。
爲了解決這個問題,讓我們嘗試顯而易見的解決方案。在 index.ts
檔案中,移除元件匯出,只匯出 TypeScript 檔案:
export { useUser } from './composables/useUser'
現在,npx unbuild
命令可以正常執行:
➜ npx unbuild ℹ Automatically detected entries: src/index [esm] [dts] ℹ Building vue-library ℹ Cleaning dist directory: ./dist ✔ Build succeeded for vue-library dist/index.mjs (total size: 139 B, chunk size: 139 B, exports: useUser) Σ Total dist size (byte size): 531 B
這是一個好的開始,但我們的元件仍然在 src
資料夾中,無論你如何仔細搜尋,它們都不會出現在 dist
資料夾中。
複製 Vue 檔案
現在 .ts
檔案已經正確處理了,我們可以嘗試使用簡單的 cp
命令將 .vue
檔案複製到 dist
資料夾中。
cp -r src/components/ dist/components/
然後,我們在 package.json
中新增一個 exports
欄位,告訴使用者在哪裏可以找到這些元件:
{ "type": "module", "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs" }, "./components/*": { "import": "./dist/components/*.vue" } }, "main": "dist/index.mjs", "types": "dist/index.d.ts" }
看起來很有希望,不是嗎?但事實並非如此。 😔
匯入路徑的困境
在我們的 Vue 元件中,useUser
可組合函式的匯入方式如下:
<script setup> import { useUser } from '../composables' </script>
而我們的 dist
資料夾的結構如下:
dist/ components/ User.vue index.d.mts index.d.ts index.mjs
你能找出問題所在嗎?👀
元件無法使用,並會產生 Cannot find module '../composables' or its corresponding type declarations
錯誤,因為路徑指向錯誤的位置。我們已經將所有 TypeScript 檔案打包到一個名為 index.mjs
的檔案中,完全丟失了專案的結構。
打包還是不打包?
從這裏,我們有兩個選擇:
使用 Vite、外掛和複雜的配置來打包 Vue 檔案。
保留目錄結構,並使用 mkdist 等工具對 TypeScript 檔案進行檔案到檔案的轉譯(無包構建)。
最終的決定取決於你的具體需求,但我相信簡潔是最好的方法。因此,讓我們探索第二個選項。
目標是將 TypeScript 檔案轉換為 JavaScript 檔案,同時保留 src
資料夾在 dist
資料夾中的原始結構,並且忽略 Vue 檔案。
注意: 使用 <script setup>
塊,mkdist 會忽略 Vue 檔案,以便 Vue 編譯器生成執行時 props。否則,它會生成一個 .vue.d.ts
檔案,為 Vue 元件提供型別。
配置 Unbuild
沒錯,我們將配置 unbuild,因為它整合了 mkdist,使其非常易於使用。
首先,在專案的根目錄中建立一個 build.config.ts
檔案。Unbuild 會讀取此配置檔案,瞭解如何處理專案。它假設了一些預設值,並從 package.json
中推斷出許多方面,但我們需要告訴它使用 mkdist。
import { defineBuildConfig } from 'unbuild' export default defineBuildConfig({ entries: ['./src/'], declaration: true, })
這是一個非常簡單的配置檔案。我很喜歡它。 entries
鍵告訴 unbuild 從哪個檔案開始轉譯。但是,./src/
是一個目錄,由末尾的 /
表示。有了這個提示,unbuild 會放棄預設的打包工具 rollup,轉而使用 mkdist。declaration
鍵提示 unbuild 建立 TypeScript 宣告檔案(.d.ts
)。
我們也可以移除 package.json
中的 exports
欄位,因為我們不再打包檔案,並恢復 index.ts
檔案中的元件匯出。
讓我們重新執行 npx unbuild
命令,見證奇蹟。
➜ npx unbuild ℹ Building vue-library ℹ Cleaning dist directory: ./dist ✔ Build succeeded for vue-library dist (total size: 600 B) └─ dist/index.d.ts (108 B) └─ dist/index.mjs (112 B) └─ dist/composables/useUser.d.ts (79 B) └─ dist/composables/useUser.mjs (124 B) └─ dist/components/User.vue (177 B) Σ Total dist size (byte size): 600 B
現在,dist
資料夾的結構如下:
dist/ components/ User.vue composables/ useUser.d.ts useUser.mjs index.d.ts index.mjs
這與 src
資料夾的結構一致,User.vue
元件現在可以在 dist
資料夾中使用。相對匯入路徑仍然有效,元件可以在任何 Vue 專案中使用。🥳
本地開發
許多教程到此為止,建議你已經準備好將包釋出到 npm 了。但是,如果你看不到工作成果,如果你不能在構建過程中進行測試,如何才能建立一個複雜的庫呢?
基本上,你無法做到。
讓我們看看如何在本地專案中使用該庫,爲了簡單起見,我們將使用同一個倉庫。
我將展示使用 pnpm workspace 的最簡單方法。
首先,在專案的根目錄中建立一個 pnpm-workspace.yaml
檔案:
packages: - . - playground
然後,在 playground
資料夾中建立一個新的 Vite 專案:
npx create-vite playground --template vue-ts
因為我們使用的是 pnpm workspace,所以要從專案的根目錄安裝依賴項:
pnpm install
注意: 此步驟不是必需的。你可以建立一個新的 Vite 專案並在其中安裝依賴項。Pnpm 透過在根目錄中安裝所有工作區的依賴項來簡化這個過程。一個簡單的 pnpm install
命令會安裝所有工作區的依賴項。
現在,我們可以在一個 "真實" 專案中使用這個 playground
庫來測試我們的庫。
例如,開啟 src/App.vue
檔案,匯入 User
元件:
<script setup> import { User } from '../../src' </script> <template> <User /> </template>
然後執行專案:
cd playground && pnpm dev
你將在瀏覽器中看到 User
元件顯示出來。🎉 如此簡單,卻又如此強大。我幾乎在構建的每個庫中都使用這種方法,它非常有效。
釋出到 npm
這一部分很簡單,只要你理解了工作流程。
首先,給你的庫命名。在本例中,我將使用 @barbapapazes/vue-library
。
接下來,在 npm 上建立一個帳戶,並使用 npm login
命令登入。
然後,安裝 changelogen 來生成變更日誌,按照語義化版本控制和提交規範更新版本號,並預先填寫 GitHub 版本釋出資訊。
pnpm i -D changeloggen
接下來,在 package.json
中新增兩個指令碼:
{ "scripts": { "prepack": "unbuild", "release": "changelogen --release && npm publish --access public && git push --follow-tags" } }
prepack
指令碼在將包釋出到 npm 之前執行 unbuild
命令。release
指令碼生成變更日誌,將包釋出到 npm,並將標籤推送到 GitHub。
注意: 你的專案應該在 GitHub 上才能使用 --release
選項。如果沒有,請省略它,並在倉庫中手動建立釋出。
現在,使用以下命令將包釋出到 npm:
npm run release
就這樣!你已經將你的第一個 Vue.js 元件庫釋出到 npm 了。🚀
準備就緒
今天就到這裏。我們已經涵蓋了許多主題:
如何使用 TypeScript 構建 Vue.js 元件庫。
如何在 TypeScript 專案中管理 Vue 檔案。
如何使用 unbuild 來轉譯 TypeScript 檔案,而不打包 Vue 檔案。
如何使用 mkdist 來保留專案結構。
如何使用 pnpm workspace 在本地專案中測試庫。
如何將庫釋出到 npm。