使用monorepo搭建项目,一般是大型项目便于管理或是多个单独项目但又有很多公用的部分,为了便于管理,统一风格,代码审查,可配置eslint、git、prettier等,只要想统一管理或统一设置的都可配置。
友情提示
- 先安装
pnpm,安装命令为npm install -g pnpm; - 我们以
monorepo命名为文件夹,后文中称根目录;
第一步:在根目录pnpm-workspace.yaml文件
pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
apps 和 packages 都是子包。
第二步:创建子包文件夹
apps:子包一般放单独的项目文件,可以是web、mobile、node serve等,我们作为测试,建立两个web端项目为testA和testB,这俩项目都是用vue-cli创建的vue2的项,目录如下:

任何项目都可,如
vue3、vite、ts等,搭建项目的逻辑相同,此处只是参考
packages:子包一般放公共组件和公共方法库,有的会把这俩放到npm 仓库,然后apps子包在下载使用,我们这里选用私包,不用发布,直接在项目里引用。
创建好后,目录结构如下:

第三步:创建 package.json 文件
3.1、在根目录执行 pnpm init -y
根目录 package.json 内容如下
{
"name": "monorepo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.34.5"
}
3.2、将apps/testA/package.json的文件修改如下:
主要是将`name`修改命名空间格式的
{
"name": "@monorepo/testA",
"version": "0.1.0",
...
}
3.2、将apps/testB/package.json的文件修改如下:
主要是将`name`修改命名空间格式的
{
"name": "@monorepo/testB",
"version": "0.1.0",
...
}
3.4、将packages/components/package.json的文件修改如下:
主要是将`name`修改命名空间格式的
{
"name": "@monorepo/components",
"version": "0.1.0",
...
}
3.5、将packages/utils/package.json的文件修改如下:
主要是将`name`修改命名空间格式的
{
"name": "@monorepo/utils",
"version": "0.1.0",
...
}
完整目录如下:

第四步:创建一个公用组件并做打包准备
4.1、在packages/component下创建一个src文件夹,并创建一个index.vue文件
packages/component/src/index.vue
<template>
<div>
我是测试组件, 哈哈哈
</div>
</template>
<script>
export default {
}
</script>
4.2、在packages/component/src文件夹下创建index.js,用于导出组件
packages/component/src/index.js
export { default as Test }
from './Test.vue'
整体目录如下:

我们将打包的文件输出在dist文件夹,用rollup作为打包工具,因此要改写packages/component/package.json,增加两个配置
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
}
这里要注意:
1、打包工具有很多,这里我们选用的是rollup
2、因为要把公共组件发布为私包,所以要将private设置为true
完整package.json内容如下:
packages/component/package.json
{
"name": "@monorepo/components",
"version": "1.0.0",
"private": true,
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"scripts": {
"build": "rollup -c"
},
"peerDependencies": {
"vue": "^2.6.11"
}
}
其中
"build": "rollup -c"是打包命令,这里先不运行,我们继续配置
peerDependencies代表由上层workspace提供
第五步:创建一个公用方法并做打包准备
在packages/utils下创建一个src文件夹,并创建一个index.js文件,导出一个测试方法
packages/utils/src/index.js
export function sum(a, b) {
return a + b;
}
完整目录如下:

我们将打包的文件输出在dist文件夹,用rollup作为打包工具,因此要改写packages/utils/package.json,增加两个配置
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
}
这里要注意:因为要把公共组件发布为私包,所以要将
private设置为true
完整package.json内容如下:
packages/utils/package.json
{
"name": "@monorepo/utils",
"version": "1.0.0",
"private": true,
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"scripts": {
"build": "rollup -c rollup.config.js"
}
}
其中
"build": "rollup.config.js"是打包命令,这里先不运行,我们继续配置
目前为止,我们打包准备做的差不多了,现在回到根目录安装rollup打包工具
第六步:安装并配置rollup打包工具
6.1、安装rollup
这是基于`Vue2 安装的
pnpm add -Dw rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-vue@5
6.2、在packages/components创建rollup.config.js,内容如下
packages/components/rollup.config.js
const resolve = require('@rollup/plugin-node-resolve')
const commonjs = require('@rollup/plugin-commonjs')
const vue = require('rollup-plugin-vue')
module.exports = {
input: 'src/index.js',
output: [{
file: 'dist/index.js',
format: 'cjs'
},
{
file: 'dist/index.mjs',
format: 'esm'
}
],
plugins: [
resolve(),
commonjs(),
vue({
css: true,
compileTemplate: true
})
],
external: ['vue']
}
目录如下:

6.3、在packages/utils创建rollup.config.js,内容如下
packages/utils/rollup.config.js
export default {
input: 'src/index.js', // 入口文件
output: [{
file: 'dist/index.js',
format: 'cjs' // commonjs,给require使用
},
{
file: 'dist/index.mjs',
format: 'esm' // import使用
}
],
// 外部依赖,不打进包里
external: []
}
目录如下:

6.4、配置根目录下的
// 运行项目 testA
"dev:testA": "pnpm --filter testA dev",
// 运行项目 testB
"dev:testB": "pnpm --filter testB dev",
// 打包项目 testA
"build:testA": "pnpm --filter testA build",
// 打包项目 testB
"build:testB": "pnpm --filter testB build",
// 同时打包项目 testA 和 testB
"build": "pnpm --filter testB build && pnpm --filter testB build",
// 打包组件库 components
"build:cmpts": "pnpm --filter @monorepo/components run build",
// 打包方法库 utils
"build:utils": "pnpm --filter @monorepo/utils build",
// 同时打包组件库 components 和 方法库 utils
"build:lib": "pnpm -r --filter @monorepo/components --filter @monorepo/utils run build"
友情提示:
--filter:是实行子包的意思
"build": "pnpm --filter testB build && pnpm --filter testB build"是同时执行两个命令但是线性的,先完成第一个才能执行第二个
"build:lib": "pnpm -r --filter @monorepo/components --filter @monorepo/utils run build"也是同时执行两个命令,但是并行的,两个同时开始
完整的根目录下的package.json内容如下:
根目录下的 package.json
{
"name": "monorepo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev:testA": "pnpm --filter testA dev",
"dev:testB": "pnpm --filter testB dev",
"build:testA": "pnpm --filter testA build",
"build:testB": "pnpm --filter testB build",
"build": "pnpm --filter testB build && pnpm --filter testB build",
"build:cmpts": "pnpm --filter @monorepo/components run build",
"build:utils": "pnpm --filter @monorepo/utils build",
"build:lib": "pnpm -r --filter @monorepo/components --filter @monorepo/utils run build"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.34.5",
"devDependencies": {
"@rollup/plugin-commonjs": "^29.0.3",
"@rollup/plugin-node-resolve": "^16.0.3",
"rollup": "^4.62.4",
"rollup-plugin-vue": "^5.1.9"
}
}
6.5 打包组件库
运行pnpm build:cmpts进行组件库打包,打包完成后会在packages/components下多出一个打包后的文件夹dist,里面有index.js和index.mjs两个文件,目录如下:

6.6、打包方法库
运行pnpm build:utils进行组件库打包,打包完成后会在packages/utils下多出一个打包后的文件夹dist,里面有index.js和index.mjs两个文件,目录如下:

6.7 配置包依赖
我们是配置的私包,因此要在项目testA和testB配置对私包packages/components、packages/utils的依赖,即分别在apps/testA/package.json和apps/testB/package.json文件的dependencies节点增加依赖,如下
...
"dependencies": {
"@monorepo/components": "workspace:*",
"@monorepo/utils": "workspace:*",
...
}
...
workspace:*星号的意思是匹配任何版本,也可以是具体版本号
完整的package.json内容如下
apps/testA/package.json
{
"name": "@monorepo/testA",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@monorepo/components": "workspace:*",
"@monorepo/utils": "workspace:*",
"core-js": "^3.6.5",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vuex": "^3.4.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.12",
"@vue/cli-plugin-router": "~4.5.12",
"@vue/cli-plugin-vuex": "~4.5.12",
"@vue/cli-service": "~4.5.12",
"prettier": "^2.2.1",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"vue-template-compiler": "^2.6.11"
}
}
apps/testB/package.json
{
"name": "@monorepo/testB",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@monorepo/components": "workspace:*",
"@monorepo/utils": "workspace:*",
"core-js": "^3.6.5",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vuex": "^3.4.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.12",
"@vue/cli-plugin-router": "~4.5.12",
"@vue/cli-plugin-vuex": "~4.5.12",
"@vue/cli-service": "~4.5.12",
"prettier": "^2.2.1",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"vue-template-compiler": "^2.6.11"
}
}
6.8、分别在apps/testA和apps/testB 运行pnpm i安装包依赖
这里要注意,每次组件库和方法库内容更新时,项目testA 和 testB 都要重新装包依赖
假设 组件库 和 方法库更新了,运行命令的顺序如下
1、打包组件库:pnpm build:cmpts
2、打包方法库:pnpm build:utils
3、项目testA下安装包依赖:pnpm i
4、项目testB下安装包依赖:pnpm i
5、启动项目testA:pnpm dev:testA
6、启动项目testB:pnpm dev:testB
第七步:测试从私库引入公共组件和方法
前面的我们已经把私包即packages/components和packages/utils进行了发布,并在项目里也配置了包依赖。接下来我们再项目页面里引入组件库做下测试,看是否成功。
7.1、运行项目testA: pnpm dev:testA
7.2、引入组件库中的Test测试组件
apps/testA/src/App.vue
<template>
<div id="app">
<div id="nav">
<p>我是 TestA</p>
</div>
<!-- 测试组件 -->
<Test />
</div>
</template>
<script>
// 引入组件
import {Test} from "@monorepo/components";
export default {
name: "TestA",
components: { Test }
};
</script>
页面展示正常,如下:

7.3、引入方法库中的sum方法
apps/testA/src/App.vue
<script>
// 引入组件
import {Test} from "@monorepo/components";
// 引入方法
import {sum} from "@monorepo/utils";
export default {
name: "TestA",
components: { Test },
mounted(){
console.log('sum方法测试:', sum(1, 3) );
}
};
</script>
页面输出如下:

组件库 和 方法库 使用正常,到此monorepo搭建私库的方法完成,项目只是提供一个思路,希望对大家有帮助!!!