轻量 Monorepo 实践:pnpm workspace 管理双独立产品
当你在九月接连开发了「AI 情绪日记」和「AI 手账排版工具」两款独立产品时,很快就会遇到一个维护上的痛点:
- 两款产品共用着同一套手绘 UI 组件库(手绘边框、和纸胶带、图章盖印动效、Toast 气泡)。
- 共用着相同的边缘 API 网关鉴权逻辑与 Zod 校验 Schema。
- 如果采用两个独立的 Git 代码仓库,每当修复了手绘边框的一个 CSS 渲染 Bug,就必须手动将代码复制粘贴到另一个仓库,繁琐且极易产生版本分叉。
引入复杂的 Turborepo、Nx 或 Lerna 对于单兵作战的独立开发者来说又太重了。
最轻量、最优雅的解法是:使用 pnpm 原生自带的pnpm-workspace打造极简 Monorepo 仓库结构。零多余配置,实现双产品的代码高复用与独立敏捷发版。
+--------------------------------------------------------------------+ | 听汐的轻量 pnpm workspace 独立产品架构 | +--------------------------------------------------------------------+ | [Root 根目录 (共享 TypeScript / Tailwind / ESLint 配置)] | | | | | ├── apps/ | | │ ├── mood-journal/ (产品 1: AI 情绪日记 Web/PWA) | | │ └── layout-maker/ (产品 2: AI 手账排版画报 Web/PWA) | | │ | | └── packages/ | | ├── ui/ (共享手绘 UI 组件库: 胶带/边框/图章) | | └── shared-core/ (共享算法: 颜色映射/DFA敏感词/Zod) | +--------------------------------------------------------------------+1. 根目录配置 pnpm-workspace.yaml
在根目录下新建pnpm-workspace.yaml,指定子包的扫描路径:
packages: - "apps/*" - "packages/*"根目录的package.json只需声明跨包执行的脚本:
{ "name": "tide-creative-tools-monorepo", "private": true, "scripts": { "dev:mood": "pnpm --filter mood-journal dev", "dev:layout": "pnpm --filter layout-maker dev", "build:all": "pnpm -r run build" } }2. 共享组件库 packages/ui 的内部软链接引用
在packages/ui中开发通用手账组件(如WashiTape.tsx),无需发布到 npm 公共仓库,直接在apps/layout-maker/package.json中通过workspace:*协议进行本地实时软链接:
{ "name": "layout-maker", "dependencies": { "@tide/ui": "workspace:*", "@tide/shared-core": "workspace:*" } }在排版产品的页面中直接优雅导入:
import React from "react"; import { WashiTape, HandDrawnCard } from "@tide/ui"; import { generateMoodGradient } from "@tide/shared-core"; export const Editor = () => { return ( <HandDrawnCard> <WashiTape /> <p>两款小产品共享同一套精雕细琢的手作组件库 ✨</p> </HandDrawnCard> ); };3. 本地开发与单项目独立部署
由于使用了 pnpm 的硬链接与内容寻址存储(Content-addressable Store):
- 无论你有多少个子应用,
node_modules里的 React 核心文件在全局磁盘上永远只占用一份物理空间。 - 在 Cloudflare Pages 部署时,指定Root directory为
apps/layout-maker,构建器会自动解析并打包引用的packages/ui,完美支持单项目独立发版。
用最轻的轮子搭起多产品的敏捷生产线,独立开发的迭代效率立刻翻倍。