tailwind解析content始终以process.cwd()为根而非配置文件位置,必须用path.resolve(__dirname, '..')构造绝对路径,显式列出各包路径及所有文件后缀,且每个子包应独立维护tailwind.config.js。

Monorepo 里共享 CSS 组件库不是“配个路径就能用”,而是必须同时处理构建时扫描、运行时加载、包间依赖协议和样式隔离四件事;漏掉任一环,就会出现类名不生成、样式冲突、本地有效 CI 失效等静默问题。
tailwind.config.js 的 content 路径为什么总扫不到 packages/ui 里的类名
Tailwind 解析 content 字段时,始终以 process.cwd()(你执行命令的目录)为根,而不是配置文件所在位置。你在 apps/web 下运行 pnpm build,但 tailwind.config.js 在 packages/tailwind-config,里面写的 "../packages/ui/src/**/*" 就会错位指向 apps/../packages/ui——根本不是 monorepo 根目录。
它也不支持动态表达式,import.meta.resolve 或 require.resolve 全部无效。唯一稳解是用 path.resolve(__dirname, '..') 构造绝对路径:
const path = require('path');
module.exports = {
content: [
path.resolve(__dirname, '../packages/ui/src/**/*.{js,ts,jsx,tsx}'),
path.resolve(__dirname, '../../apps/web/src/**/*.{js,ts,jsx,tsx}'),
path.resolve(__dirname, './src/**/*.{js,ts,jsx,tsx}'),
],
};
-
__dirname永远指向配置文件自身位置,不受执行目录影响 - 每个包应维护自己的
tailwind.config.js,避免多版本 Tailwind 行为不一致 - 后缀必须显式列出:
.svelte、.astro、.mdx漏一个就扫不到 - 通配符如
./packages/**/src/**在 v3/v4 中不可靠,必须逐条写死路径
为什么用 workspace: 协议安装 tailwind-config 却还是报 Cannot use import statement outside a module
Tailwind CLI 启动时同步 require() 配置文件,只认 CommonJS。哪怕你写的是 tailwind.config.ts,只要最终导出是 export default config,Node 就会当作 ESM 加载,直接抛错。
必须满足两个条件:
- 导出形式是
module.exports = config,不能用export default - 不能在配置文件里
import其他模块(包括你自己写的 TS 工具函数),否则触发 ESM 陷阱
最稳妥做法:直接用 tailwind.config.js,纯 JS,零编译干扰。若坚持用 TS,得确保该文件被 ts-node 提前编译成 JS,且输出仍是 module.exports 形式。
另外,业务包中必须用 require("tailwind-config") 引入,不能 import——Tailwind 不支持 ESM 配置入口。
UnoCSS / Windi CSS 在 Monorepo 里为什么也扫不到跨包子组件
UnoCSS 不是 Tailwind 的平替,它是另一套独立扫描逻辑,默认只扫描当前工作区目录(即 process.cwd())。它不会自动感知 packages/ui 是被引用的依赖,更不会去读它的源码。
解决方式和 Tailwind 类似,但细节不同:
- 必须显式扩展
content或include数组,指向所有含 JSX/TSX 的包路径 - 某些插件(如
@unocss/webpack)需额外配置transformers才能识别defineConfig中的路径 - 若用
unocss-preset-uno,注意它默认不启用attributify和webFonts,跨包组件若依赖这些特性,得手动打开
和 Tailwind 一样,它也不报错——扫不到就是没样式,最终 CSS 文件小得可疑。
CSS 隔离在微前端 + Monorepo 场景下为何 scoped 属性会丢失
Vue 的 scoped 或 Svelte 的 :global 控制,本质依赖构建时注入 data-v-xxx 属性,并让 CSS 选择器带上该属性匹配。但在微前端中,子应用常由主应用通过 innerHTML 或 appendChild 动态挂载,DOM 节点迁移后,原 Vue 实例绑定的属性不会自动带过去,导致选择器失效。
真正可靠的解法是分层处理:
- 组件级隔离:用 CSS Modules 或
.razor.css(Blazor),哈希类名 + JS 显式绑定,不依赖 DOM 属性生命周期 - 应用级隔离:微前端框架(如 qiankun)开启样式沙箱,或手动插入
<style></style>标签并加命名空间前缀 - 设计系统级隔离:用
@layer分层 + 自定义属性(--color-primary)+@scope(Chrome 119+)做边界控制
最容易被忽略的是:构建产物中若包含第三方 UI 库(如 shadcn-ui)的未编译源码,它们的 class 名可能被 Tailwind 扫到,但又没走你的隔离逻辑——结果是部分样式隔离了,部分却全局污染。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











