项目里有一套会长期维护的主题、设计 token 或共享 UI 组件时,把文件复制到每个业务项目里,刚开始很省事,后面往往会变成同步负担:改了一处样式,还要记得去其他地方补上。
pnpm workspace 提供了一个更顺手的组织方式:把项目和主题包放进同一个仓库,让它们直接共享主题源码。
先看目录
一个常见的结构大致是这样:
my-app/
├─ src/ # 页面、路由和业务代码
├─ packages/
│ └─ my-theme/ # 主题与共享 UI 的源码
├─ package.json
├─ pnpm-workspace.yaml
└─ pnpm-lock.yaml
这里的 packages/my-theme 是一个正常的 npm 包,只是它和业务项目住在同一个仓库里。主题的 CSS、组件、图片和字体都只维护在这里。
workspace 做了什么
在根目录创建 pnpm-workspace.yaml,告诉 pnpm 哪些目录属于同一个工作区:
packages:
- packages/*
主题包也需要自己的 package.json,其中最重要的是包名:
{
"name": "@acme/theme",
"version": "0.1.0"
}
根目录的业务项目再把它声明为依赖:
{
"dependencies": {
"@acme/theme": "workspace:*"
}
}
workspace:* 的意思可以理解为:使用当前仓库里同名的包。执行 pnpm install 后,pnpm 会把两者连接起来,因此本地改动会立刻被业务项目使用。
日常开发时会发生什么
假设主题包里有这些文件:
packages/my-theme/
├─ src/
│ ├─ components/
│ └─ theme/
│ ├─ tokens.css
│ └─ styles.css
└─ package.json
之后修改 tokens.css、主题样式或共享组件,项目读取到的就是更新后的内容,不需要再复制文件,也没有额外的同步步骤。
代码可以通过包名引用组件:
import { Button, ThemeProvider } from '@acme/theme';
CSS 也可以从包中导入:
@import '@acme/theme/theme.css';
具体入口文件叫什么,取决于主题包在 package.json 中暴露了哪些 exports。如果导入失败,通常先检查包名、入口文件和 exports 配置。
文件该放在哪里
可以用一个简单的判断来划分边界:会被多个项目复用、并且希望统一维护的内容,放进主题包;只服务于当前产品的内容,留在业务项目里。
| 放进主题包 | 留在业务项目 |
|---|---|
| 设计 token、颜色、字体规则 | 页面与路由 |
| 通用主题 CSS | 接口请求与业务状态 |
| Button、Dialog 这类共享组件 | 只在当前页面使用的组件 |
| 通用图标与主题资源 | 产品文案与业务逻辑 |
这样改主题时,关注点会很明确;业务代码也不会被一套可复用的基础样式牵着走。
部署时需要注意什么
workspace 并不只在本地有效。将主题包源码、pnpm-workspace.yaml 和 pnpm-lock.yaml 一起提交到仓库后,Vercel 之类的平台在仓库根目录安装和构建即可:
pnpm install --frozen-lockfile
pnpm build
pnpm 会安装 workspace 内的依赖,构建工具再把被 CSS 或 JavaScript 引用到的主题资源一并处理进产物。
小结
workspace 的重点在于让多个包从一开始就指向同一份源码。对于需要长期迭代的主题或组件库,这能减少重复文件,也让更新路径更清楚。
気に入ったならばコメントを残してくださいね~