monorepo抽离shadcn和tailwind

最小可shadcn可初始化环境

一般我们会将shadcn集成到现有的前端框架项目中,比如vite、nextjs等等。
但是如果我们要把shadcn抽离出来,成为一个单独的组件库项目,供多个项目使用,那么我们就要创建一个最小可运行的环境,来支持shadcn初始化以及后续的组件安装。

我们在执行pnpm dlx shadcn init的时候,会进行一些检查,如果不符合要求,是不能进行初始化的,通过执行pnpm dlx shadcn init,如果出现下面的提示,则说明我们当前的环境是符合shadcn初始化要求的。

✔ Preflight checks.
✔ Verifying framework. Found Vite.
✔ Validating Tailwind CSS config. Found v4.
✔ Validating import alias.

Preflight checks

预检检查,会检查项目中是否已经被初始化过。
这个不重要!
不过我们给我们的最小可初始化环境项目 初始化一下pnpm init
项目将名为shadcn-comps 包名为@repo/shadcn-comps

// packages>shadcn-comps>package.json
{
  "name": "@repo/shadcn-compnts",
  "version": "0.0.0",
  "description": "",
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Verifying framework

验证框架,会检查项目中是否使用了vite、nextjs等框架。
所以我们只需要在项目中新建一个空的vite.config.js文件即可,这样shadcn就会识别为vite项目了。

Validating Tailwind CSS config

验证tailwindcss配置,会检查项目中是否已经安装了tailwindcss,并且在项目中存在任意一css文件且有@import "tailwindcss";

那我们就先安装tailwind吧pnpm add tailwindcss,然后再创建样式文件!

/* packages>shadcn-comps>src>base-styles.css */
|--src
    |--base-styles.css

其内容如下

/* packages>shadcn-comps>src>base-styles.css */

@import "tailwindcss";

Validating import alias

验证导入别名,会检查项目中是否已经配置了导入别名。
需要我们创建一个最小的tsconfig.json文件,内容如下:

{
  "compilerOptions": {
    "jsx": "react", // 防止编辑器报错不识别jsx语法
    "paths": {
      // @repo/shadcn-comps为项目的包名,即package.json的name属性,如果非多仓,则@直接对应root即可
      "@repo/shadcn-comps/*": ["./src/*"]
    }
  }
}

这会决定一会初始化shadcn时候期生成配置文件components.json中aliases的值。
而配置文件components.json中aliases的值又会决定组件下载路径。

安装shadcn组件

现在你已经配置好所有的shadcn可具备初始化的环境了。我们立刻pnpm dlx shadcn init进行shadcn初始化吧,
它会在你配置的tsconfig.jsonpaths中新建一个@repo/xxx/*的别名对应的目录下:

  • 新建一个components目录,用于存放组件。
  • 新建一个lib目录,用于存放工具函数,比如utils.ts。

最后你可以pnpm dlx shadcn add button来安装一个组件了。

对了,我们现在可以将 vite.config.js删除了,毕竟他只是为了让shadcn初始化的时候识别框架类型而已!

导出并使用

我们需要将shadcn-comps内容导出

// packages\shadcn-comps\package.json
{
  "name": "@repo/shadcn-comps",
  "exports": {
    "./*": "./src/components/ui/*.tsx",
    "./hooks/*": "./src/hooks/*.ts",
    "./lib/*": "./src/lib/*.ts"
   },
   // ...
}

最后我们在另一个项目 @repo/web中即可安装并使用此包!

// apps\web\src\pages\home.tsx
import { Button } from "@repo/shadcn-comps/button";

export default function Home() {
  return (
    <div>
      <Button>呵呵</Button>
    </div>
  );
}

优化(可选)

防止react报错

在shadcn-comps中,因为shadcn组件内有导入react,为了防止编辑器报错我们可以安装pnpm add -D @types/react

抽离tailwind

实际上,在安装完毕后你还可以将 tailwindcss 卸载,然后创建一个 tailwind-config的单仓,将刚生成的components.json里的 css配置 改为向tailwind-config单仓索取

shadcn-comps>components.json

{
  {
    //...
    "tailwind": {
      // ...
      "css": "@repo/tailwind-config",
    },
  }
}

这样以来,我们的的shadcn组件仓@repo/shadcn-comps将变的更为干净,不再包含tailwind相关内容,请看下面谈 如何抽离tailwind!

抽离tailwind

因为多个仓库可能都会用到tailwind,所以我们可以将tailwind抽离出来,成为一个单独的仓库,供多个仓库使用。
我们初始化一个仓库,包名改为@repo/tailwind-config,然后安装 tailwind依赖!

@repo/shadcn-comps中的初始化后的xxx.css(即包含@import "tailwindcss";的那个css文件)拷贝到@repo/tailwind-config中,并加上如下代码

/* 只要你在任意一个会被 Tailwind 扫描到的 CSS 文件里写了 @source,它就把这些路径追加到扫描列表,效果与在被v4抛弃的 tailwind.config.ts 里写 content 完全等价。 */
@source "../**/*.{ts,tsx}";
@source "../../../../apps/**/*.{ts,tsx}";
@source "../../../../packages/**/*.{ts,tsx}";

最后导出!

{
  "name": "@repo/tailwind-config",
  "version": "0.0.0",
  "type": "module",
  "private": true,
  "exports": {
    ".": "./base-styles.css",
    "./postcss": "./postcss.config.js" //可选
  },
  "devDependencies": {
    "postcss": "^8.5.6", //可选
    "tailwindcss": "^4.1.16"
  }
}

这样我们在其它仓库包中,就可以直接使用了:
比如@repo/web中,可以直接导入到这个主项目的全局样式文件style.css中,而不要在安装tailwindcss包了(但是如果@tailwindcss/vite还是要用的)

@import "@repo/tailwind-config";

在tailwind中,也可以直接给shadcn指定改依赖的css,而不要在安装tailwindcss包了

对了 pnpm add tw-animate-css ,这歌也用得到!

"tailwind": {
      // ...
      "css": "@repo/tailwind-config",
},

经过验证,抽离后,只需要主项目@repo/web中的全局样式文件导入即可,tailwind的css貌似配置与否都不重要!

其它补充

@import "tailwindcss"

🚨 魔法语句,不是文件路径,
这并非导入tailwindcss这个style文件(实际上也没有这个文件)

@import "tailwindcss"; 只是 “按下开关”——
它让 Vite 去调 @tailwindcss/vite 里预先注册好的 虚拟模块
于是插件开始 扫描 → 交给 Tailwind 核心 → 生成一段 CSS → 塞回页面

这条语句本身不拉文件、也不含样式
纯粹是 “嘿,Tailwind,可以开工了” 的触发口令。

这是让大部分人误解的地方:

看到@import "tailwindcss"就是觉得,这不就是导入了tailwindcss预设了的一堆样式字典嘛,就跟bootstrap或者layui一样!这就给我造成困惑 那@tailwindcss/vite干啥的,而且为啥每次看head中关于tailwindcss的样式集合预设还不一样?

@source

这个是取代v3版本,tailwind-config的content字段的,用来标记tailwind的扫描范围的(默认情况下哎之扫描当前项目,如果是多仓库那子项目中的tailwind部分就不会被扫描到。旦“被 Tailwind 扫描到”就会被tailwind构建(一些tailwind专属语法比如 @theme @apply 等)输出和生效了。

需要注意的是哪怕你当前项目的css文件显示@import了其它模块里的含tailwind语法的css文件,但是只要此css文件没有被当前项目扫描到,那么其中的tailwind语法是不会被tailwind构建输出的。必须显式的指定扫描到子项目!

/* shared-styles.css */
.some-component {
  @apply bg-blue-500 p-4;  /* 这里的 @apply 需要被扫描到才会生效 */
}

/* main.css */
@import "./shared-styles.css";  /* ❌ 这样不会让 Tailwind 扫描 shared-styles.css */
@import "tailwindcss";

/* 必须在 @source 中显式包含 */
@source "./shared-styles.css";  /* ✅ 这样才会扫描其中的 Tailwind 语法 */
posted @ 2025-11-06 13:05  丁少华  阅读(9)  评论(0)    收藏  举报