PC 项目兼容 uni-app:基于 pnpm Monorepo 的工程化重构实践
1️ 为什么要引入 Monorepo?
原始状态:
一个 PC Web 项目(Vue3 + Vite + TS)
业务逻辑与 UI 强耦合
当需要支持:
- uni-app H5
- 小程序
- App → 只能复制项目,维护成本极高
目标:
一套业务逻辑,多端 UI 实现
2️ Monorepo 的整体结构设计
最终结构如下:
root/
├─ apps/
│ ├─ web/ # 原 PC Web 项目
│ └─ uni/ # uni-app 项目
├─ packages/
│ ├─ core/ # 核心业务逻辑
│ ├─ api/ # 接口抽象
│ ├─ store/ # 状态逻辑
│ └─ utils/ # 工具函数
├─ pnpm-workspace.yaml
├─ tsconfig.base.json
├─ package.json
└─ node_modules/职责划分非常明确:
apps:只关心「运行在哪个平台」packages:只关心「业务逻辑本身」
3️ pnpm-workspace.yaml 是什么?起什么作用?
3.1 文件内容
packages:
- apps/*
- packages/*3.2 它解决了什么问题?
pnpm-workspace.yaml 的作用只有一个,但非常关键:
声明:哪些目录是“workspace 子项目”
效果:
pnpm install 时:
- 会扫描这些目录
- 识别每一个
package.json - 建立统一的依赖图
子项目之间可以:
- 相互依赖
- 使用 workspace link
- 不需要发布到 npm
4️ packages 包如何配置(非常重要)
4.1 每个 package 都是一个“标准 npm 包”
以 packages/core 为例:
packages/core/
├─ src/
│ └─ index.ts
└─ package.json{
"name": "@zkzq/core",
"version": "0.0.0",
"main": "src/index.ts",
"types": "src/index.ts"
}关键点:
- 必须有
name - 推荐使用 scoped name(如
@zkzq/core) - 不需要真的发布,只用于 workspace 内部
4.2 packages 中应该放什么?
✅ 推荐放:
- 业务流程(useXXX)
- 接口抽象
- 状态计算
- 纯 TS 工具函数
❌ 不推荐放:
.vue文件- DOM 操作
uni.*API- 条件编译
5️ 全局配置如何统一?
5.1 统一 TypeScript 配置
根目录:
// tsconfig.base.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"baseUrl": ".",
"paths": {
"@zkzq/*": ["packages/*/src"]
}
}
}子项目继承:
// apps/web/tsconfig.json
{
"extends": "../../tsconfig.base.json"
}5.2 统一依赖版本(推荐)
// root/package.json
{
"devDependencies": {
"typescript": "^5.4.0",
"pnpm": "^9.0.0"
}
}6️ packages 如何变成子项目的依赖?
这是 Monorepo 的核心使用方式。
6.1 给某个子项目单独安装 packages 包(推荐)
根目录执行:
pnpm add @zkzq/core --filter web--filter:过滤找到 web 子项目
pnpm add @zkzq/core -F uni-template --workspace--workspace:只允许从 workspace 解析这个包,不准访问 npm registry
或者:
pnpm add @zkzq/core --filter ./apps/web效果:
- 依赖写入
apps/web/package.json - 实际使用的是 workspace 本地包
- 不会从 npm 下载
生成的依赖:
"@zkzq/core": "workspace:*"或者不使用安装命令:直接在 package.json 写依赖,不用 add
{
"dependencies": {
"@zkzq/core": "workspace:*"
}
}然后:
pnpm install6.2 在子项目中直接使用
import { login } from "@zkzq/core";6.3 子项目之间互相依赖也是一样的方式
pnpm add @zkzq/utils --filter uni7️ scripts:如何在根目录运行子项目?
7.1 在子项目中定义 scripts
// apps/web/package.json
{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}7.2 在根目录统一调度(推荐)
// root/package.json
{
"scripts": {
"dev:web": "pnpm --filter web dev",
"dev:uni": "pnpm --filter uni dev",
"build:web": "pnpm --filter web build",
"build:uni": "pnpm --filter uni build"
}
}使用:
pnpm dev:web
pnpm dev:uni7.3 并行 / 递归执行(高级用法)
pnpm -r build或:
pnpm --filter web... buildpnpm 会根据依赖关系自动排序。
8️ 为什么选择这种方式?
最终收益:
- 一套业务逻辑,多端复用
- Web / uni 职责清晰
- 重构成本可控
- 后续新增端成本极低
9️ 本篇总结
Monorepo 不是“目录合并”, 而是“工程系统重组”
通过:
pnpm-workspace.yaml- packages 规范
- scripts 调度
- workspace 依赖
可以让 PC 项目 自然演进为多端工程体系。
后续碰到问题以及了解: