用 pnpm workspace 维护项目与主题包

Published 2026-09-01 00:00 833 words 5 min read

This post is not yet available in English. Showing the original.
用一份简单的笔记理解 pnpm workspace:把主题做成独立包,让项目始终使用同一份源码。

项目里有一套会长期维护的主题、设计 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 的重点在于让多个包从一开始就指向同一份源码。对于需要长期迭代的主题或组件库,这能减少重复文件,也让更新路径更清楚。

If you enjoyed this, leave a comment~