为什么 uni-app 在 Monorepo 中必须做「环境区分」,而 Web 项目却不用?
—— 从 workspace 依赖、监听机制到工程边界的一次完整拆解
一、背景:一个非常常见的工程升级场景
很多团队都会遇到类似的情况:
原本有一套 PC Web 项目
现在需要:
- 兼容小程序
- 兼容 H5
- 兼容 App
技术选型:uni-app(Vue3 + Vite + TS)
为了避免逻辑重复,自然会想到:
抽离业务逻辑为共享包, 用 Monorepo 统一管理
于是工程结构演进为:
.
├─ apps/
│ ├─ web/ # 原 PC Web 项目
│ └─ uni/ # uni-app 项目
├─ packages/
│ └─ core/ # 共享业务逻辑
├─ pnpm-workspace.yaml
└─ package.json二、Web 项目:为什么「什么都不配」也能正常开发 + 打包?
1. pnpm workspace 已经把依赖“连好了”
在 Web 子项目中:
import { xxx } from "@zkzq/core";pnpm 会在:
apps/web/node_modules/@zkzq/core创建一个 软链接,指向:
packages/core2. Vite 对 workspace 包“非常友好”
在 开发模式(dev) 下,Vite 有一个重要特性:
如果依赖是 workspace 本地包, 且是 ESM 源码 → 当作源码处理
结果是:
- 修改
packages/core/src - Web 项目立即重新编译
- 不需要 alias
- 不需要额外配置
👉 这就是 Web 项目“看起来什么都不用做”的根本原因
3. Web 项目为什么 build 阶段也总是“最新的”?
因为:
- build 读取的是软链接指向的真实文件
- 永远是磁盘当前状态
- 不存在“缓存旧包”的情况
所以: Web:dev 省心,build 也省心
三、uni-app:问题从哪里开始?
1. uni 的 Vite ≠ 原生 Vite
虽然 uni 使用的是 vite-plugin-uni,但它:
- 接管了模块解析
- 接管了依赖分析
- 接管了监听范围
- 接管了多端编译流程
uni 在开发期有一个非常关键的判断:
这个文件是不是 uni 项目的“源码”?
2. workspace 包在 uni 看来是什么?
在 uni 的视角中:
apps/uni/src/**→ 源码 ✅packages/core/**→ 依赖 ❌
结果是:
- 文件确实发生了变化
- 但 不进入 uni 的依赖图
- 不触发重新编译
- 页面自然不会更新
3. 不同端的监听差异(非常关键)
| 运行端 | uni 的监听策略 |
|---|---|
| H5 | 会监听部分入口文件(如 index.html),但不会重编译 workspace 包 |
| 小程序 | 只监听 src/,workspace 包完全被忽略 |
这也是为什么:
同一套 Monorepo Web 正常 uni H5 半正常 uni 小程序完全无效
四、为什么给 uni 配 alias 就“好了”?
resolve: {
alias: {
"@zkzq/core": "../../packages/core/src"
}
}这一步的本质是:
强制把共享包“伪装成” uni 项目的源码
结果:
- 进入 uni 的依赖图
- 被纳入监听范围
- 修改即重新编译
- 开发体验恢复正常
五、那为什么 build 阶段不推荐继续 alias 到 src?
这是工程边界问题,而不是“能不能跑”的问题。
1. alias src 在 build 阶段会破坏什么?
- 绕过 package.json
- 绕过 exports / main
- 绕过依赖版本边界
- apps 不再“依赖一个包”,而是“引用一堆路径”
👉 Monorepo 名存实亡
2. uni build 是否会打包到最新代码?
会的,而且一定会。
原因:
- workspace 依赖是软链接
- build 读取的是实时文件系统
- 不区分 root / 子项目执行
所以正确结论是:
alias src 不是为了解决 build 正确性, 而是为了 dev 期监听能力
六、正确且推荐的工程模式
✅ 开发期(Dev)
const isDev = process.env.NODE_ENV === "development";
resolve: {
alias: isDev ? { "@zkzq/core": "../../packages/core/src" } : {};
}目标:开发体验最大化
✅ 构建期(Build)
import { xxx } from "@zkzq/core";目标:依赖边界清晰、产物稳定、可迁移
七、为什么说「uni 必须做环境分析,而 Web 不需要」?
本质原因只有一句话:
Web 的构建工具帮你承担了 Monorepo 的复杂度, uni 没有,也不能。
所以:
- Web:工具“聪明”
- uni:工程师必须“清醒”
八、最终总结(一句话带走)
uni-app 在 Monorepo 中需要环境区分, 不是因为它麻烦, 而是因为它对工程边界更敏感。