SSF0SSF0
首页
前端
  • Node
  • Go
  • Python
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
  • 开源项目
Timeline
Github
标签
分类
首页
前端
  • Node
  • Go
  • Python
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
  • 开源项目
Timeline
Github
标签
分类
  • HTML

    • html1
    • html2
  • CSS

    • Flex 布局常见问题与解决方案
  • JavaScript

    • 数据类型及引用问题
    • 处理 Blob 类型文件下载问题总结
    • localStorage 与 sessionStorage 区别
    • JavaScript 中的 script 标签问题详解
    • JavaScript 中的 this 指向问题详解
    • SessionStorage踩坑记录
    • 动态加载 JS 脚本方法对比
    • 浏览器页面关闭场景下的数据上报
    • HTTP流式接口详解与封装实践
    • JavaScript 中 this 绑定与解构:为什么 Pinia Store 可以解构,但 Class 实例方法不行?
  • es6

    • Promise
    • es6 模块导出方式全解析
    • reduce
    • forEach 中使用 async/await 的异步陷阱
  • Vue2

    • created VS mounted 发起异步请求
    • vue2-2
  • Vue3

    • Vite + Vue3 修改 element-plus 源码
    • Vue v-if 与 v-show
    • Vue3 ref 获取组件
    • Vue3 路由传参
    • 父子组件与组件里 Hooks 加载顺序
    • 第三方组件传入参数TS提示
    • Vue3 Props 在 Hooks 中的响应性处理
    • Vue Router 的两种历史模式及部署配置详解
    • 在Vue 3项目中顺利集成Tailwind CSS的完整指南
    • Vue Scoped CSS 深度解析
    • Vue 3 Teleport 组件完全指南:从原理到实战
    • Vant List 组件中双向绑定 loading 的注意事项
    • Vue 3 + Vite 中的路径别名与静态资源处理深度解析
    • 获取图片高度
    • 深入理解 Pinia:storeToRefs vs toRefs - 避开 TypeScript 类型推断的坑
  • webSocket

    • WebSocket 鉴权入门(实战版):握手票据 / 首帧鉴权 + 连接级 cid
  • Electron

    • 快速构建 electron + vue3 项目
  • TS

    • TS 泛型
    • 记录模板使用断言的问题
    • type 与 interface
  • WebPack

    • Webpack 介绍
  • Vite

    • Vite CLI 常见命令
    • vite 与 webpack 比较
    • Vite base 配置与 public 静态资源路径问题
  • 客户端数据库

    • IndexedDB 索引陷阱:字段重命名后索引返回旧数据
    • IndexedDB 事务陷阱与最佳实践:TransactionInactiveError 完全解析
  • UniApp

    • uni-app + Monorepo + 微信小程序热更新失效问题排查与结论总结
    • Monorepo + uni-app 开发中监听 / 热更新机制差异与解决方案
    • uni-app 运行与编译原理梳理
    • 为什么 uni-app 在 Monorepo 中必须做「环境区分」,而 Web 项目却不用?
  • 项目工程

    • 前端代码风格
    • Vue3 项目规范
    • npm 镜像问题
    • 包管理工具
    • 使用 engines 限制开发环境依赖
    • 打包与shell交互指定模式
    • Monorepo示例
    • PC 项目兼容 uni-app:基于 pnpm Monorepo 的工程化重构实践
    • Vue3 + Vite + pnpm Monorepo 迁移问题指南
    • 前端工程化:深入理解 npm 依赖管理机制
    • pnpm 修改依赖源码打包报错
    • 代码片段
  • 移动端

    • 浏览器跳转外部应用时请求状态码问题
  • 记录一下小程序
  • 控制浏览器正确保存网站账号密码的技巧
  • 信息泄露
  • 使用Gulp实现前端项目哈希化压缩打包
  • 前端缓存策略与文件哈希化的关联解析
  • 浏览器刷新方式与 Vue 生命周期、缓存行为解析
  • 浏览器消息铃声实现记录

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 install

6.2 在子项目中直接使用

import { login } from "@zkzq/core";

6.3 子项目之间互相依赖也是一样的方式

pnpm add @zkzq/utils --filter uni

7️ 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:uni

7.3 并行 / 递归执行(高级用法)

pnpm -r build

或:

pnpm --filter web... build

pnpm 会根据依赖关系自动排序。


8️ 为什么选择这种方式?

最终收益:

  • 一套业务逻辑,多端复用
  • Web / uni 职责清晰
  • 重构成本可控
  • 后续新增端成本极低

9️ 本篇总结

Monorepo 不是“目录合并”, 而是“工程系统重组”

通过:

  • pnpm-workspace.yaml
  • packages 规范
  • scripts 调度
  • workspace 依赖

可以让 PC 项目 自然演进为多端工程体系。

后续碰到问题以及了解:

  • Vue3 + Vite + pnpm Monorepo 迁移问题指南
  • uniapp 不同创建方式运行的原理
  • monorepo 里 uniapp 共享组件热更新问题
  • 微信小程序热更新异常排查
最后更新时间:
贡献者: 何风顺
上一页
Monorepo示例
下一页
Vue3 + Vite + pnpm Monorepo 迁移问题指南