Monorepo项目是一种将多个项目或模块集中管理在单个代码仓库中的软件开发和代码管理策略模式,它具有简化代码共享、统一版本控制、提升协作效率和降低维护成本等优势,Monorepo使得跨项目共享代码变得更加容易和高效,因为所有项目都在同一个仓库中,可以方便地引用和重用公共模块或组件。但它也可能带来一些挑战,如项目体积增大、构建时间延长等。
创建一个monorepo项目
一、初始化一个项目
新建一个文件夹 确认安装node(带npm)、pnpm;在文件下执行pnpm init,初始化项目生成一个package.json文件
pnpm init
二、配置pnpm-workspace.yaml文件
pnpm-workspace.yaml 文件是 Pnpm(一个快速、节省磁盘空间的包管理工具)用于定义工作区(monorepo)结构的配置文件。此文件允许你指定哪些目录应该被 Pnpm 视为工作区的一部分,从而可以集中管理这些包的依赖和脚本。
packages:
# all packages in direct subdirs of packages 独立项目/
- 'packages/*'
# all packages in subdirs of components 多项目公用组件/
- 'components/**'
# exclude packages that are inside test directories
- '!**/test/**'
# 公共api -axios
- 'apis/**'
# utils工具库
- 'utils/**'
注:在根目录下使用pnpm install xxx 会报错:ERR_PNPM_ADDING_TO_ROOT...,不要在根目录下安装某一个子应用下具体依赖,要在具体项目目录下安装。
这条警告信息是在告诉你,当你尝试在 monorepo 的根目录添加依赖时,这个操作会将依赖添加到工作区的根目录,这可能不是你想要的结果。如果你确实有意这么做,你应该再次运行这个命令,并加上 -w 或 --workspace-root 标志来明确表示你的意图。
在 monorepo 中,通常的做法是在每个子包的 package.json 文件中添加依赖,而不是在根目录添加。这样,每个包可以有自己独立的依赖版本,避免了版本冲突。
如果你确实需要在根目录添加一些全局的脚本或工具依赖,并且理解这样做的后果,你可以按照警告的指示,使用 -w 或 --workspace-root 标志来执行命令:
pnpm add <package-name> -w
或
pnpm add <package-name> --workspace-root
如果你想要忽略这个警告,并且你确信在根目录添加依赖是合适的,你可以设置 ignore-workspace-root-check 配置项。这可以通过在根目录的 .npmrc 文件或者环境变量中设置来实现。在 .npmrc 文件中添加:ignore-workspace-root-check=true。请注意,忽略这个检查可能会导致依赖管理上的混乱,所以你应该谨慎使用这个功能,并确保你理解这样做的后果。
三、创建相关工具及应用
新建目录包含:utils、apis、components、packages;
1、初始化子包:utils、apis不需要vue等脚手架,可以直接使用vite初始化一个ts项目:
pnpm create vite
// 选择vanilla-ts 创建原生ts项目,没有框架集成
2、修改name:子包在package.json中的name一般常用@projectName/childName:
3、新增入口文件index.ts:apis、utils应用需要在package.json文件添加配置入口文件‘main’:‘index.ts’;
同时,在index.ts文件中导出apis,utils中封装的方法等内容:
4、初始化components:用vue3脚手架创建,同时初始化两个子应用,web1、web2
npm create vue\@latest
// 也可以直接用npm create vite 选择vue,默认vue最新版本
5、引用公共api:web1、web2引用apis、utils中的api时,可以在相应项目目录下执行依赖安装
注:在安装依赖是子工具包必须在pnpm-workspace.yaml文件中被指定,否则安装不识别:
执行:pnpm add @test/apis,对应package.json文件中会自动添加dependencies:
6、初始化componemts组件包:在components目录下新增入口文件index.ts,并在package.json中配置,新增组件后在index.ts中导入并导出:
在组件中引入:
// CBtn组件
<template>
<div class="button" @click="showMsg">{{ msg }}</div>
</template>
<script setup lang="ts">
const props = defineProps<{ msg: string }>()
const emits = defineEmits<{ (e: 'confirm', msg: string): void }>()
const showMsg = () => { emits('confirm', props.msg) }
</script>
<style scoped>
.button {
width: 30px;
height: 15px;
border: 1px solid red;
}
</style>
// 引用
<script setup lang="ts">
import Axios from "@test/apis/axios";
import { CBtn } from "@test/components";
const onclick = (msg: string)=>{
console.log(msg);
}
</script>
<template>
<div>
<CBtn msg="hello" @confirm="onclick" />
</div>
</template>
<style scoped></style>