前言

1、workspace:pnpm中使用workspace协议的目的是将拒绝解析除本地 workspace 包含的 package 之外的任何内容。也就是说如果引入的项目需要强制使用本地包,则可以使用workspace:协议 。

2、微前端和Monorepo的关系:Monorepo是一种单仓库多应用的代码管理方式。微前端是一种实现了多个子项目间样式隔离、JS隔离、路由、子项目通信等功能的项目架构模式。微前端使用Monorepo进行代码管理可以实现共享工程配置,同时又快捷地共享模块代码的功能。

3、pnpm:在使用monorepo方式管理项目时,会产生诸多问题,如幽灵依赖、依赖安装耗时的问题,需要使用pnpm进行解决这类问题。

所以,当仅仅需要对多个子项目间共享工程配置和模块间代码共享时使用Monorepo进行代码管理即可。

而当需要继承多个不同栈的模块或服务、每个模块需要独立开发部署、需要支持独立的生命周期和版本控制等时使用微前端。

在开发项目的过程中,我们需要在一个仓库中管理多个项目,每个项目有独立的依赖、脚手架,这种形式的项目结构我们称之为Monorepo,pnpm workspace就是管理这类项目的方案之一。

一、pnpm

pnpm代表performance npm(高性能的npmn),同npm和yarn,都属于javascript包管理安装工具,它较npm和yarn在性能上得到很大提升,被称为 快速地,节省磁盘空间的包管理工具。

二、对比lerna+yarn

使用lerna+yarn组合,也可以实现Monorepo项目管理。但是相比来说,更推荐pnpm workspace来管理。

三、构建一个Monorepo

先上最终的项目结构:

1
2
3
4
5
Monorepo
- umi-antd
- web
-- common1
-- common2

如上图,我们最终要创建如上图的一个项目结构,其中umi-antd是主项目,web文件夹下的都是子项目,供umi-antd调用。

1、创建主项目

主项目这里是使用的umi项目,这里也可以构建基于其他打包工具的各类项目,只要是遵守package.json配置条例的项目,都是可以的。

构建umi项目

1
pnpm dlx create-umi@latest

这里选择创建Ant Design Pro项目。

1
2
3
4
5
$ npx create-umi@latest
? Pick Umi App Template › - Use arrow-keys. Return to submit.
Simple App
❯ Ant Design Pro
Vue Simple App

2、创建子项目common1

  • 进入Monorepo>web目录,创建common1文件夹
  • 然后进入common1目录,执行命令npm init对项目进行初始化,这时候会生产一个package.json文件

新建index.ts文件,代码如下:

1
2
3
export default function print(msg:string) {
console.log(msg);
}

3、创建子项目common2

步骤和创建common1一致

4、主项目中引入子项目

安装common1和common2。

1
2
pnpm -F umi-antd add common1
pnpm -F umi-antd add common2

安装完成后,在umi-antd依赖中出现两个子项目的软链接(或者叫符号链接)。

在业务代码中调用子项目输出的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//umi-antd\src\pages\Home\index.tsx

import Guide from '@/components/Guide';
import { trim } from '@/utils/format';
import { PageContainer } from '@ant-design/pro-components';
import { useModel } from '@umijs/max';
import styles from './index.less';
import print1 from 'common1';
import print2 from 'common2';

const HomePage: React.FC = () => {
const { name } = useModel('global');
print1("umi-antd中调用common1");
print2("umi-antd中调用common2");
return (
<PageContainer ghost>
<div className={styles.container}>
<Guide name={trim(name)} />
</div>
</PageContainer>
);
};

export default HomePage;

5、运行主项目

到这里基本搭建完毕,这时候的整体项目结构如图:

然后执行命令启动主项目:pnpm -F umi-antd start,我们可以看到,控制台打印出来子模块中的信息。

6、子模块调用子模块

子模块也是可以相互调用的,这里我们在common1中引入common2的方法。

  • 执行命令pnpm -F common1 add common2

  • 在common1中调用common2中方法,代码如下:

    1
    2
    3
    4
    5
    6
    //web\common1\index.ts
    import print2 from 'common2';
    export default function print(msg:string) {
    console.log(msg);
    print2("这里是common1中调用common2");
    }

再次运行umi-antd项目时,我们可以看到,控制台已经打印了common2中方法。

四、workspace协议(workspace:)

1、协议概述

默认情况下,如果可用的 packages 与已声明的可用范围相匹配,pnpm 将从工作区链接这些 packages。 例如, 如果bar引用”foo”: “^1.0.0”并且foo@1.0.0存在工作区,那么pnpm会从工作区将foo@1.0.0链接到bar。 但是,如果 bar 的依赖项中有 “foo”: “2.0.0”,而 foo@2.0.0 在工作空间中并不存在,则将从 npm registry 安装 foo@2.0.0 。 这种行为带来了一些不确定性。

幸运的是,pnpm 支持 workspace 协议 workspace: 。 当使用此协议时,pnpm 将拒绝解析除本地 workspace 包含的 package 之外的任何内容。 因此,如果您设置为 “foo”: “workspace:2.0.0” 时,安装将会失败,因为 “foo@2.0.0“ 不存在于此 workspace 中。

当 link-workspace-packages 选项被设置为 false 时,这个协议将特别有用。 在这种情况下,仅当使用 workspace: 协议声明依赖,pnpm 才会从此 workspace 链接所需的包。

以上是官网的解释,实际在使用的时候,如果引入的项目需要强制使用本地包,则可以使用workspace:协议。改造下umi-antd的package.json文件如下:

1
2
3
4
5
6
7
8
"dependencies": {
"@ant-design/icons": "^5.0.1",
"@ant-design/pro-components": "^2.4.4",
"@umijs/max": "^4.0.76",
"antd": "^5.4.0",
"common1": "workspace:^",
"common2": "workspace:^"
},

上面加上common1和common2的依赖声明后,会强制到本地寻找,找不到也不会到npm registry对应的私仓去下载。

2、版本符号

workspace协议,区分*、~、^、版本号,表示的意义各部相同,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"dependencies": {
"foo": "workspace:*",
"bar": "workspace:~",
"qar": "workspace:^",
"zoo": "workspace:^1.5.0"
}
}
// 将会被转化为:
{
"dependencies": {
"foo": "1.5.0",
"bar": "~1.5.0",
"qar": "^1.5.0",
"zoo": "^1.5.0"
}
}

五、Monorepo工具有哪些?

Monorepo 是一种代码管理策略,它将一个项目的所有代码存储在一个单独的版本控制库中。Monorepo 解决了多个问题:解决了代码共享与重复的困扰,提供更好的可追踪性和一致性。通过使用Monorepo,开发团队可以更好地组织、共享和管理代码,提高开发效率和协作质量。选择合适的Monorepo工具并灵活运用,能够帮助开发者更好地应对复杂的软件开发需求,推动项目的成功完成。

参考:

Hello Monorepo