依赖共享的解决方案pnpm + monorepo

5,735 阅读4分钟

参考文档

Monorepo

Monorepo 是将多个包(package)统一放置在一个仓库(repo)进行管理的方式。这种方式可以更好地管理包之间的依赖与兼容,统一的提交日志也更容易追踪问题。许多大公司如 Google、Facebook 都采用 Monorepo 方式管理的多个前端包。

pnpm

pnpm 是一个快速、节省磁盘空间的包管理工具。它通过硬链接在 node_modules 中共享依赖,避免了重复安装同一个包的多个版本,这大大节省了磁盘空间。

Monorepo + pnpm

在 Monorepo 项目中使用 pnpm 具有以下优势:

  1. 空间节省:通过硬链接的方式,无需重复安装包,这在有许多包的 Monorepo 项目中特别有效。
  2. 依赖共享:不同包可以共享依赖,简化依赖管理,避免重复依赖的安装与升级。
  3. 统一依赖:pnpm 以锁定文件(pnpm-lock.yaml)来管理依赖版本,这可以促进 Monorepo 中的包采用同一版本的依赖。
  4. 快速安装:pnpm 通过硬链接,只需安装一次包,后续的安装会非常快速,这很适合 Monorepo 频繁开发与构建的场景。

使用者

许多知名公司和开源项目都采用 Pnpm + Monorepo 的方式来管理项目,比如:

  1. Babel:Babel 是一个广泛使用的 JS 编译器,它采用 Pnpm 和 Monorepo 来管理众多包和插件。
  2. React:React 这个至关重要的前端框架也是使用 Pnpm 和 Monorepo 构建的,它有上百个内部包需要管理。
  3. Vue:Vue 框架同样采用 Pnpm 和 Monorepo,以管理其众多工具、框架、UI 库等。
  4. Next.js:Next.js 这个流行的 React 框架也是采用 Pnpm 管理 Monorepo 的结构构建的。

步骤

电脑环境 node@16
安装 pnpm
npm install -g pnpm

// 查看对应的版本
pnpm -v
创建 workspace
pnpm init
根目录创建 pnpm-workspace.yaml 重要
packages:
  # 公用依赖
  - "pl-core/**"
  # 各个项目
  - "projects/**"

创建自己的目录结构

依赖独自分开暴露

├── libs
│   ├── core
│   │   ├── src  // 含有一些apis
│   │   └── package.json
│   │   └── index.js // 对外暴露
│   ├── ui
│   │   ├── src // 一些ui组件
│   │   ├── package.json
│   │   └── index.js // 对外暴露
│   ├── util
│   │   ├── src // 一些工具类
│   │   ├── package.json
│   │   └── index.js // 对外暴露
├── projects
│   ├── p1  //实际的项目
│   │   ├── src
│   │   └── package.json
│   ├── p2  //实际的项目
│   │   ├── src
│   │   └── package.json
├── package.json //全局第三方公用包管理,如 axios element-ui
├── pnpm-lock.yaml
└── pnpm-workspace.yaml // 配置 项目+依赖的公用模块

依赖集中式对外暴露

├── pl-core
│   ├── src // 含有一些 apis utils components
│   ├── package.json  // 默认暴露出口 index.js
│   ├── index.js  // 对外暴露
├── projects  //实际的项目
│   ├── p1
│   │   ├── src
│   │   └── package.json
│   ├── p2
│   │   ├── src
│   │   └── package.json
├── package.json //全局第三方公用包管理,如axios element-ui
├── pnpm-lock.yaml
└── pnpm-workspace.yaml // 配置 项目+依赖的公用模块
pnpm 常用指令
// 初始化项目 生成 package.json
pnpm init
// 添加依赖包
pnpm add [package.json的name]
// pnpm 查看源
pnpm config get registry
// 切换淘宝源
pnpm config set registry https://registry.npmmirror.com
// 全局依赖(供全局使用)
pnpm i element-plus -w
// 移除依赖
pnpm uninstall @libs/ui
根目录

技术选型后全局安装公用依赖

// 技术选型如 vue3 + vite + element-plus
// 根目录执行指令
pnpm i element-plus axios  -w
创建 pl-core 核心模块
  • 创建以及初始化
npm init vue@latest
npm i
npm run dev
  • 在 src 编写 apis utils components
// /src/components/Count/index.vue
<template>
  <p>count:{{ count }}</p>
  <button @click="count++">点击我++</button>
</template>
<script setup>
import { ref } from "vue";
const count = ref(0);
</script>

// /src/utils/sum.js
export const sum = (...rest) => rest.reduce((pre, cur) => pre + cur, 0)
  • 新建 index.js 对外暴露 apis utils components
// pl-core/index.js

import Count from "./src/components/Count/index.vue";
export { sum } from "./src/utils/sum";
export { Count };
新建 projects 目录,用来存放各个项目
  • 1.创建项目(项目技术选型最好跟 pl-core 目录下的选型一样,譬如 axios 版本 vue 版本 element 版本)
npm init vue@latest
npm i
npm run dev
  • 2.切换到 projects 对应的项目目录下,添加并安装 pl-core 重要
pnpm add [pl-core/package.json的name]

// 譬如
pnpm add pl-core
  • 3.安装成功后,使用依赖
<template>
  <div>
    <h3>p1</h3>
    <Count />
  </div>
</template>

<script setup>
import { Count, sum } from "pl-core";
console.log("sum", sum(1, 2, 3));
</script>
  • 4.针对 pl-core 导出组件时第三方样式丢失的问题,可以在引入的项目里添加对应的样式
构建产物
在projects/p1各个项目中构建,
npm run build

构建的产物中会自动包含有libs目录下的资源
http-server dist
一些方便操作

根目录 package.json 文件 配置 启动||构建 各个项目的指令

"scripts": {
    "run:p1": "pnpm -F \"p1\" -r dev", //运行 projects/p1 项目中的package.json 对应的 dev 指令
    "run:p2": "pnpm -F \"p2\" -r dev",
    "build:p1": "pnpm -F \"p1\" -r build",
    "build:p2": "pnpm -F \"p2\" -r build"
  },