教你如何使用monorepo快速搭建一个项目

使用monorepo搭建项目,一般是大型项目便于管理或是多个单独项目但又有很多公用的部分,为了便于管理,统一风格,代码审查,可配置eslint、git、prettier等,只要想统一管理或统一设置的都可配置。

友情提示

  • 先安装pnpm,安装命令为npm install -g pnpm
  • 我们以monorepo命名为文件夹,后文中称根目录

第一步:在根目录pnpm-workspace.yaml文件

pnpm-workspace.yaml

packages:
    - 'apps/*'
    - 'packages/*'

appspackages 都是子包。

第二步:创建子包文件夹

apps:子包一般放单独的项目文件,可以是web、mobile、node serve等,我们作为测试,建立两个web端项目为testAtestB,这俩项目都是用vue-cli创建的vue2的项,目录如下:

image.png

任何项目都可,如vue3、vite、ts等,搭建项目的逻辑相同,此处只是参考

packages:子包一般放公共组件和公共方法库,有的会把这俩放到npm 仓库,然后apps子包在下载使用,我们这里选用私包,不用发布,直接在项目里引用。

创建好后,目录结构如下:


image.png

第三步:创建 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",
  ...
}

完整目录如下:


image.png

第四步:创建一个公用组件并做打包准备

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'

整体目录如下:


image.png

我们将打包的文件输出在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;
}

完整目录如下:


image.png

我们将打包的文件输出在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']
}

目录如下:


image.png

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: []
}

目录如下:


image.png

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.jsindex.mjs两个文件,目录如下:

image.png

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

image.png

6.7 配置包依赖
我们是配置的私包,因此要在项目testAtestB配置对私包packages/componentspackages/utils的依赖,即分别在apps/testA/package.jsonapps/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/testAapps/testB 运行pnpm i安装包依赖

这里要注意,每次组件库和方法库内容更新时,项目testA 和 testB 都要重新装包依赖
假设 组件库 和 方法库更新了,运行命令的顺序如下
1、打包组件库:pnpm build:cmpts
2、打包方法库:pnpm build:utils
3、项目testA下安装包依赖:pnpm i
4、项目testB下安装包依赖:pnpm i
5、启动项目testApnpm dev:testA
6、启动项目testBpnpm dev:testB

第七步:测试从私库引入公共组件和方法

前面的我们已经把私包即packages/componentspackages/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>

页面展示正常,如下:


image.png

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>

页面输出如下:


image.png

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容