戰(zhàn):用 pnpm Workspaces + Turborepo 搭建共享 React 組件庫(kù))
shadcn/ui Astro Monorepo 模板實(shí)戰(zhàn)用 pnpm Workspaces Turborepo 搭建共享 React 組件庫(kù)【免費(fèi)下載鏈接】uiA set of beautifully-designed, accessible components and a code distribution platform. Works with your favorite frameworks. Open Source. Open Code.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ui/ui本篇基于倉(cāng)庫(kù)中的 Astro Monorepo 模板說(shuō)明文檔完整解析 shadcn/ui 官方提供的 Astro React TypeScript Monorepo 模板apps/web中運(yùn)行 Astro 應(yīng)用packages/ui中以 workspace 包形式沉淀共享的 shadcn/ui 組件。讀完后你可以掌握在 pnpm workspace 中配置workspace/ui跨包引用、理解兩份components.json的差異、用 shadcn CLI 向共享包添加組件并在.astro文件中通過(guò)client:load正確掛載 React 組件的完整方案。模板定位應(yīng)用與 UI 包分層模板的核心設(shè)計(jì)是把“會(huì)變化的界面邏輯”和“可復(fù)用的 UI 組件”拆開目錄角色說(shuō)明apps/webAstro 應(yīng)用頁(yè)面、布局依賴workspace/ui消費(fèi)共享組件packages/ui共享 UI 包workspace/ui存放 shadcn/ui 生成的組件、hooks、工具函數(shù)與全局樣式根目錄 templates/astro-monorepo/package.json 聲明了模板的運(yùn)行基線{ packageManager: pnpm10.33.4, engines: { node: 22.12.0 }, devDependencies: { prettier: ^3.8.3, prettier-plugin-astro: ^0.14.1, prettier-plugin-tailwindcss: ^0.8.0, turbo: ^2.9.18, typescript: ~6 } }即pnpm 10 Node ≥ 22.12 Turborepo 2.9 TypeScript ~6。apps/web/package.json 則給出了具體框架版本astro ^7、astrojs/react ^5、react/react-dom ^19.2.6、tailwindcss/vite ^4Tailwind v4并且以workspace/ui: workspace:*的方式聲明對(duì)共享包的工作區(qū)依賴。pnpm workspace 與構(gòu)建腳本白名單pnpm-workspace.yaml 把a(bǔ)pps/*與packages/*納入同一工作區(qū)packages: - apps/* - packages/* allowBuilds: esbuild: true sharp: true msw: falseallowBuilds字段用于控制依賴的安裝后構(gòu)建腳本模板放行esbuild、sharp禁用msw避免不需要的依賴在安裝階段執(zhí)行任意腳本。Turborepo根目錄的統(tǒng)一任務(wù)入口turbo.json 定義了根目錄五個(gè)腳本build/dev/lint/format/typecheck見根 package.json背后的任務(wù)編排{ ui: tui, tasks: { build: { dependsOn: [^build], inputs: [$TURBO_DEFAULT$, .env*], outputs: [dist/**] }, lint: { dependsOn: [^lint] }, format: { dependsOn: [^format] }, typecheck: { dependsOn: [^typecheck] }, dev: { cache: false, persistent: true } } }幾個(gè)關(guān)鍵點(diǎn)build聲明了dependsOn: [^build]即先構(gòu)建被依賴的上游包packages/ui再構(gòu)建消費(fèi)方apps/web并以dist/**作為緩存產(chǎn)物dev標(biāo)記為persistent: true且關(guān)閉緩存保證pnpm dev能持續(xù)運(yùn)行 Astro 開發(fā)服務(wù)器而不被 Turborepo 殺掉lint/format/typecheck同樣按依賴拓?fù)渑判騼蓚€(gè)包會(huì)各自執(zhí)行本目錄定義的對(duì)應(yīng)腳本例如 apps/web/package.json 的typecheck是astro check而 packages/ui/package.json 的是tsc --noEmit。因此日常只需在倉(cāng)庫(kù)根目錄執(zhí)行pnpm dev/pnpm build/pnpm typecheck等命令Turborepo 負(fù)責(zé)分發(fā)到各子包??绨玫娜龑优渲脀orkspace/ui模板讓apps/web能以“按文件路徑”的方式引用packages/ui中的源碼無(wú)需構(gòu)建產(chǎn)物這一能力由三層配置共同支撐。1. 包級(jí)exports子路徑映射packages/ui/package.json 的exports把包內(nèi)子目錄逐一暴露為子路徑exports: { ./globals.css: ./src/styles/globals.css, ./lib/*: ./src/lib/*.ts, ./components/*: ./src/components/*.tsx, ./hooks/*: ./src/hooks/*.ts }這意味著import { Button } from workspace/ui/components/button會(huì)被 Node/Vite 直接解析到packages/ui/src/components/button.tsx源文件——組件是“源碼直連”而非編譯后的dist產(chǎn)物這也是該模板里packages/ui沒有build腳本的原因。2. 應(yīng)用側(cè) TypeScript 路徑映射apps/web/tsconfig.json 在astro/tsconfigs/strict基礎(chǔ)上補(bǔ)了兩組pathscompilerOptions: { jsx: react-jsx, jsxImportSource: react, paths: { /*: [./src/*], workspace/ui/*: [../../packages/ui/src/*] } }workspace/ui/*映射到packages/ui/src/*后編輯器與類型檢查可以直接跳轉(zhuǎn)到共享包源碼/*則是 Astro 側(cè)的常規(guī)src別名。packages/ui/tsconfig.json 內(nèi)部也做了對(duì)稱配置workspace/ui/*: [./src/*]使包內(nèi)自引用在類型層面一致。3. Tailwind v4 的source掃描范圍Tailwind v4 是 CSS-first 配置無(wú)tailwind.config文件樣式入口是 packages/ui/src/styles/globals.cssimport tailwindcss; source ../../../apps/**/*.{ts,tsx,astro}; source ../../../components/**/*.{ts,tsx}; source ../**/*.{ts,tsx};由于組件源碼散落在兩個(gè)包、多個(gè)目錄里Tailwind 默認(rèn)的單目錄掃描不足以收集全部候選類名因此這里用source顯式聲明應(yīng)用側(cè)所有.ts/.tsx/.astro文件、包內(nèi)components與包內(nèi)全部 TS 源碼都是掃描對(duì)象。缺了這幾行跨包組件的類名很可能不會(huì)被生成。shadcn 配置兩份components.json的分工模板中apps/web與packages/ui各有一份components.json它們都使用同一套參數(shù)語(yǔ)義但指向不同的落盤位置。應(yīng)用側(cè)把生成物路由到共享包apps/web/components.json{ $schema: https://ui.shadcn.com/schema.json, style: radix-nova, rsc: false, tsx: true, tailwind: { config: , css: ../../packages/ui/src/styles/globals.css, baseColor: neutral, cssVariables: true }, iconLibrary: lucide, aliases: { components: /components, hooks: /hooks, lib: /lib, utils: workspace/ui/lib/utils, ui: workspace/ui/components } }逐條看關(guān)鍵參數(shù)style: radix-nova組件視覺風(fēng)格rsc: falseAstro 場(chǎng)景不存在 React Server Componentsshadcn 生成代碼時(shí)不會(huì)添加use client指令——在 Astro 中組件是否水合由client:*指令決定見后文tailwind.css指向跨包路徑../../packages/ui/src/styles/globals.css從apps/web執(zhí)行 shadcn 命令時(shí)CLI 會(huì)去修改共享包里的全局樣式而不是應(yīng)用本地文件保證主題變量單一來(lái)源baseColor: neutralcssVariables: true基礎(chǔ)色板為 neutral主題以 CSS 變量形式注入便于運(yùn)行時(shí)換膚iconLibrary: lucide圖標(biāo)統(tǒng)一來(lái)自 lucidealiases中components/hooks/lib仍指向應(yīng)用自身/...但utils與ui指向workspace/ui——生成的組件依賴cn等工具函數(shù)時(shí)會(huì)引用共享包組件本體則可落在應(yīng)用內(nèi)。共享包側(cè)以包根為錨點(diǎn)的鏡像配置packages/ui/components.json 與上表的主要差異只有tailwind.css本地相對(duì)路徑src/styles/globals.css和aliasesaliases: { components: workspace/ui/components, utils: workspace/ui/lib/utils, hooks: workspace/ui/hooks, lib: workspace/ui/lib, ui: workspace/ui/components }也就是說(shuō)若直接在packages/ui目錄上下文中運(yùn)行 shadcn 命令所有產(chǎn)物都會(huì)寫入packages/ui內(nèi)部且包內(nèi)文件互相引用時(shí)也使用workspace/ui/...別名。兩份配置互為鏡像、錨點(diǎn)不同共同保證“無(wú)論在哪一層執(zhí)行 CLI引用解析都收斂到同一套路徑”。添加組件shadcn add 命令按照 README 的說(shuō)明在倉(cāng)庫(kù)根目錄執(zhí)行npx shadcnlatest add button -c apps/web-c apps/web將命令上下文定位到apps/webCLI 讀取該目錄的 components.json 完成配置解析組件代碼按其ui別名workspace/ui/components對(duì)應(yīng)的exports映射最終寫入packages/ui/src/components/成為整個(gè)工作區(qū)共享的組件。從當(dāng)前倉(cāng)庫(kù)快照看packages/ui/src/components 目錄尚為空hooks、lib亦同而 apps/web/src/pages/index.astro 已預(yù)置了對(duì)button組件的引用——模板預(yù)期的使用順序即先執(zhí)行上述add命令生成組件文件應(yīng)用即可直接跑起來(lái)。在 Astro 中使用組件README 給出的最小用法是在.astro文件中直接導(dǎo)入并使用組件--- import { Button } from workspace/ui/components/button --- html langen head meta charsetutf-8 / meta nameviewport contentwidthdevice-width / titleAstro App/title /head body div classgrid h-screen place-items-center content-center ButtonButton/Button /div /body /html模板實(shí)際首頁(yè) index.astro 在此基礎(chǔ)上展示了一個(gè)關(guān)鍵的 Astro 集成細(xì)節(jié)--- import workspace/ui/globals.css import { Button } from workspace/ui/components/button --- body ... Button client:load classNamemt-2Button/Button /body兩點(diǎn)值得注意client:load指令shadcn/ui 組件大量依賴 Radix 原語(yǔ)與客戶端交互折疊、彈出、焦點(diǎn)管理等。在 Astro 中不加client:*的 React 組件只渲染為靜態(tài) HTML加上client:load后 Astro 會(huì)在頁(yè)面加載時(shí)水合該組件交互能力才真正生效。這正是components.json里rsc: false的另一層含義——在 Astro 里客戶端行為的開關(guān)在模板指令而不是use client。樣式全局導(dǎo)入一次首頁(yè)與 main.astro 布局均通過(guò)import workspace/ui/globals.css引入全局樣式它經(jīng)由 packages/ui/package.json 的./globals.css導(dǎo)出解析到共享包內(nèi)的 globals.cssTailwind 主題與source掃描規(guī)則隨之生效。應(yīng)用側(cè)的 Vite 配置也很簡(jiǎn)潔astro.config.mjs 僅掛了兩個(gè)插件tailwindcss/viteTailwind v4 的 Vite 集成與astrojs/reactReact 渲染集成import tailwindcss from tailwindcss/vite import { defineConfig } from astro/config import react from astrojs/react export default defineConfig({ vite: { plugins: [tailwindcss()] }, integrations: [react()], })關(guān)鍵文件速查文件職責(zé)pnpm-workspace.yaml工作區(qū)成員與依賴構(gòu)建腳本白名單turbo.jsonbuild / dev / lint / format / typecheck 任務(wù)編排packages/ui/package.jsonworkspace/ui的exports子路徑映射源碼直連packages/ui/components.json包級(jí) shadcn 配置產(chǎn)物落在包內(nèi)packages/ui/src/styles/globals.cssTailwind v4 入口與source掃描范圍apps/web/components.json應(yīng)用級(jí) shadcn 配置CSS 指向共享包apps/web/tsconfig.jsonworkspace/ui/*→packages/ui/src/*類型路徑映射apps/web/astro.config.mjsTailwind vite 插件 React 集成apps/web/src/pages/index.astro組件用法示例client:load水合小結(jié)這個(gè)模板的精髓在于“單一樣式源、單一組件源、多層別名收斂”Tailwind 主題只存在于packages/ui/src/styles/globals.cssshadcn 生成的組件通過(guò)exportspathssource三層配置被應(yīng)用無(wú)縫引用Turborepo 則把多包項(xiàng)目的日常命令壓縮為根目錄一條指令。理解了 apps/web/components.json 與 packages/ui/components.json 的分工以及client:load在 Astro 中的水合作用就可以在此基礎(chǔ)上持續(xù)向packages/ui添加組件并擴(kuò)展到多個(gè)前端應(yīng)用。【免費(fèi)下載鏈接】uiA set of beautifully-designed, accessible components and a code distribution platform. Works with your favorite frameworks. Open Source. Open Code.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ui/ui創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考