一,前置准备
1、需要选定一个包管理器(yarn、yarn2、npm、pnpm)。
包管理器之间的分析请看:文档monorepo相关概念。(在前一篇文章)
最好使用 pnpm 作为包管理器。全局安装pnpm。
npm install -g pnpm
二,使用 pnpm 创建一个 monorepo 仓库
1、新建目录并初始化该目录为git仓库
mkdir monorepo-dri // 创建根目录
cd monorepo-dri // 进入根目录
pnpm init // 初始化项目
2、初始化仓库后,目录中生成package.json。接下来在生成的package.json文件中修改、删除、新增配置来限制使用pnpm等。
{
"name": "monorepo-dri", // 目录所在文件 的名称
"version": "1.0.0",
"description": "",
"private": true, // 用来限制该包为私有包,避免 npm 整包发布
"engines": {
"node": ">=16",
"pnpm": ">=7" // 限制 pnpm 版本为 7 以上。
},
"main": "index.js", // 该配置可删除
"scripts": { // 该配置可删除
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
3、在项目根目录新建 pnpm-workspace.yaml 文件,这个文件定义了工作空间的根目录,然后写入以下内容
packages:
// common文件夹(包)下的所有文件夹(包)
- 'common/**'
// libs文件夹(包)下的所有文件夹(包)
- 'libs/**'
// packages文件夹(包)下的所有文件夹(包)
- 'packages/**'
// test文件夹(包)下的所有文件夹(包)排除在外
- '!**/test/**'
// 注意:-后面有一个空格,然后再写'文件夹/**'
接下来创建目录common 、libs 、packages 。
mkdir common && mkdir libs && mkdir packages
我们的应用创建在 packages 目录下,而 libs 目录则是存放一些公共的方法(如需要发布的包)等内容、而common 目录则是存放一些公共组件和方法等内容。
我们的应用创建在 packages 目录下,而 libs 目录则是存放一些公共的方法(如需要发布的包)等内容、而common 目录则是存放一些公共组件和方法等内容。
4、主包基本目录结构
至此,我们的主包基本搭建完成。其基本目录结构如下:
├── common
├── libs
├── package.json
├── packages
└── pnpm-workspace.yaml
5、接下来在packages 目录下创建第一个应用、第二个应用、第三个应用
在 packages 目录中创建第一个应用,以 vite 为例,进入 packages 目录执行
pnpm create vite app-base --template vue-ts 命令,来创建一个名为 app-base 的项目。
pnpm create vite app-base --template vue-ts
接下来修改刚刚创建的应用 app-base 的包名,方便以后使用:名称为 @主包名/子包名。
{
"name": "@monorepo-dri/app-base"
}
此时的结构如下:
同上流程,进行创建第二个应用与第三个应用。修改新创建的两个应用的package.json文件的包名,方便以后使用。
// 创建第二个应用
pnpm create vite app1 --template vue-ts
// 创建第三个应用
pnpm create vite app2 --template vue-ts
app1应用的package.json文件
{
"name": "@monorepo-dri/app1"
}
app2应用的package.json文件
{
"name": "@monorepo-dri/app2"
}
6、复制 packages 目录中的任意一个应用的package.json文件的这几个配置项到根目录的package.json。
{
...
"scripts":{
"dev":"vite",
"build":"vue-tsc && vite build",
"preview":"vite preview"
},
"dependencies":{
"vue":"^3.4.19"
},
"devDependencies":{
"@vitejs/plugin-vue":"^5.0.4",
"typescript":"^5.2.2",
"vite":"^5.1.4",
"vue-tsc":"^1.8.27"
}
}
7、接下来修改packages 目录下的所有应用的package.json文件
子应用一定要删除以下这些配置项。如果子应用中不删除这些配置依赖的配置项,那么后续使用 pnpm install 在根目录安装模块包时,这些子应用中也会自动下载 node_modules,这样子应用中模块包就又重复安装了。
{
"dependencies":{
"vue":"^3.4.19"
},
"devDependencies":{
"@vitejs/plugin-vue":"^5.0.4",
"typescript":"^5.2.2",
"vite":"^5.1.4",
"vue-tsc":"^1.8.27"
}
}
8、运行 pnpm install 来安装依赖
8.1、在项目根目录下运行 pnpm install 来 一次性安装所有依赖
在根目录下运行 pnpm install 命令,它会自动安装所有依赖,他会找到根目录的 package.json、各个文件夹下的子应用中的 package.json,然后根据各个package.json中的依赖项的配置去安装依赖包(模块包)。
在根目录下安装的依赖,在所有的packages中都可以使用(各个子应用可以使用根目录中的依赖包)。
pnpm i
8.2、为 具体的某一个应用 安装指定依赖,以及模块之间的相互依赖。
如果需要为 具体的某一个应用安装依赖,如何处理? ——> pnpm 提供了 --filter 参数。
其中 --filter 过滤包名,具体使用可以参考 pnpm --filter。
给具体的子应用单独安装指定依赖,例如 axios。——具体使用看 标题9
pnpm add axios --filter 应用的名称(就是子应用的package.json中的name对象的值)
模块之间的相互依赖。 给具体的子应用单独安装指定依赖,例如dayjs。——具体使用看 标题15
pnpm install dayjs --filter 应用的名称(就是子应用的package.json中的name对象的值)
在设置依赖版本的时候推荐用 workspace:* ,这样就可以保持依赖的版本是工作空间里最新版本,不需要每次手动更新依赖版本
9、在 libs 目录中,再来创建一个工具函数,例如 axios
在 libs 目录创建 utils/request 目录,并创建文件 index.js 作为入口文件。在request 目录进行初始化,得到一个 packages.json 文件,将这个packages.json 文件的name修改为@monorepo-dri/request 。
为名为 @monorepo-dri/request 的子应用安装指定依赖,以 axios为例,并在 index.js 中导出一个 axios 实例。
pnpm add axios --filter @monorepo-dri/request
import axios from 'axios';
const request = axios.create({});
export default request;
此时的结构如下:
10、为各个子应用分别在vite.config.ts中设置端口号
app-base文件夹:应用名称为 ——> @monorepo-dri/app-base
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
plugins:[vue()],
server:{
port:5573
}
})
app1文件夹:应用名称为 ——> @monorepo-dri/app1
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
plugins:[vue()],
server:{
port:5574
}
})
app2文件夹:应用名称为 ——> @monorepo-dri/app2
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
plugins:[vue()],
server:{
port:5575
}
})
11、根目录的package.json配置修改
主要修改package.json文件的 scripts 配置项
{
...
"scripts":{
"dev":"vite",
"dev-base": "pnpm run -C packages/app-base dev",
"dev-app1": "pnpm run -C packages/app1 dev",
"ev-app2": "pnpm run -C packages/app2 dev",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
...
}
12、至此,包目录基本完成,进入根目录安装 typescript、eslint 等公用的包,添加一些其他文件的配置
在根目录安装依赖包的命令:
pnpm add eslint @antfu/eslint-config typescript -Dw
注意:该命令末尾的 -Dw 不能丢,不然会报警告。
并在项目根目录下新建 .eslintrc.js文件,写入如下内容
process.env.ESLINT_TSCONFIG = 'tsconfig.json'
module.exports = {
extends: '@antfu',
}
13、进入 packages/app-base 目录,安装我们刚刚创建的 request 依赖
13.1、一种方式是进入子包进行安装。
(待补充)
13.2、另一种方式是通过 --filter 来指定包名。
pnpm -F @monorepo-dri/app-base add @monorepo-dri/request@\*
该命令表示在包名为 @monorepo-dri/app-base 的包中安装 @monorepo-dri/request 依赖。
14、接下来即可在 @monorepo-dri/app-base 项目中使用@monorepo-dri/request包
<script setup lang="ts">
import request from '@kklearn/request'
const hitokoto = ref<string>('')
async function reflush() {
const result = await request.get('https://v1.hitokoto.cn')
hitokoto.value = result.data.hitokoto
}
onMounted(() => reflush())
</script>
15、pnpm公共组件库
在common目录新建components文件夹,components文件夹内部放一些公用组件或方法等。
15.1、初始化components目录
common/components
pnpm init
15.2、在components目录新建index.js文件,导出一个方法:一个格式化日期的函数,下面的 15.7 标题会用到。
common/components/index.js
import dayjs from 'dayjs'
export function formatDate(value, format = 'YYYY-MM-DD') {
return dayjs(value).format(format)
}
注意:此处通过es6模块化引入手法引入dayjs,那么就需要先下载依赖包dayjs,不然无法运行。
pnpm install dayjs -F @monorepo-dri/components
-F 等价于 --filter。
此时的结构如下:
15.3、创建一个名叫snow的文件夹(以后的组件)
snow目录内新建 index.vue 组件。
common/components/snow/index.vue
<template>
<div>我是snow组件,来自common目录的components文件夹,其内部有一个组件库叫snow,入口文件是一个vue文件index.vue</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
15.4、修改 package.json 文件
{
// 主要修改的就是name
"name": "@monorepo-dir/components",
"version": "1.0.0",
"description": "组件库内部组件snow",
"private": true,
"keywords": [],
"author": "",
"license": "MIT",
"main": "index.ts"
}
15.5、安装 workspace 依赖
下边的命令是在根目录安装 @monorepo-dir/components 这个组件,所有子项目都可以使用@monorepo-dir/components 下面的所有组件及方法等。
pnpm i @monorepo-dir/components -w
此时的结构如下:
也可以通过 --filter 指定 子应用 @monorepo-dir/app-base 安装 组件 @monorepo-dir/components 。
pnpm i @monorepo-dri/components --filter @monorepo-dri/app-base
子应用 @monorepo-dir/app-base安装组件 @monorepo-dir/components 后 子应用目录的 package.json文件 增加依赖,子项目 node_modules 增加依赖,子项目同样运行成功。
15.6、使用组件snow
app-base/src/App.vue引入snow组件并使用snow组件。
启动项目 pnpm run dev-base,运行成功,组件内容已使用成功。
效果图:
15.7、app-base 子应用中引用 common/components/index.js,并使用该文件导出的格式化时间的方法
效果图:
三、部署