Monorepo多项目管理不再难!从零开始:pnpm workspace 手把手教你打造灵活、可扩展的开发环境

8,840 阅读8分钟

一、什么是Monorepo? 定义与概念

Monorepo,即“单个仓库多项目管理”,是一种项目代码管理方式,它将多个项目或模块的代码集中管理在一个仓库中。这种方式有助于简化代码共享、版本控制、构建和部署等方面的复杂性,并提供更好的可重用性和协作性。

需求描述

我们计划利用pnpm workspace功能,实现一个高效的多项目管理方案,具体需求如下:

1. 多项目整合与独立运行

  • 将多个项目整合到一个单一的代码仓库中,便于集中管理和版本控制。
  • 提供灵活的运行机制,允许一次运行多个项目或仅运行其中的某一个项目。
  • 确保每个项目在结构上保持独立,可以自由地加入或退出整合的仓库,实现项目的可分可合,灵活流转。

2. 共享样式与组件

  • 提取多个项目中共有的样式和组件,将它们放置在workspace的根目录下。
  • 通过pnpm workspace的依赖管理机制,使这些共享的样式和组件能够被所有项目所引用和使用。
  • 避免在每个项目中重复编写相同的样式和组件代码,提高代码复用率,降低维护成本。

实现步骤

一、多项目搭建-配置-运行

1、创建目录(例如:在D盘创建一个workspace文件夹,并进入文件夹)

mkdir workspace 
cd workspace

2、初始化项目

  • 执行 pnpm init 来初始化一个 package.json 文件。这将是整个工作空间的根目录配置文件。
pnpm init
  • 初始化完成会有一个package.json文件,如下图:

juejin1.png

3、配置package.josn

  • 删除package.json 文件中的maintest字段、并且添加"private:true"

juejin2.png

4、创建 pnpm-workspace.yaml 文件

  • 手动创建一个 pnpm-workspace.yaml 文件,用于配置 packages。这个文件告诉 pnpm 哪些文件夹包含项目。例如:

juejin3.png

5、创建目录packages, 这个文件用来放你的项目,可以放一个或多个

  • 这里的目录名字可以自定义
  • 但是无论自定义什么名字都要和刚刚pnpm-workspace.yaml 文件里面配置的名字一样
  • 比如你叫apps,那么pnpm-workspace.yaml 文件下的配置就要变成packages: - 'apps/*'

juejin4.png

6、在packages文件下创建你需要的项目

  • 比如创建一个app-first
//创建第一个项目
pnpm create vite app-first --template vue
  • 再创建一个app-second
//创建第二个项目
pnpm create vite app-second --template vue
  • 创建成功后的目录结构

juejin5.png

7、设置不同的端口号

  • 为了避免端口冲突,分别在两个项目的vite.config.js中设置不同的端口号
  • 比如app-first/vite.config.js 设置:
 
server: {
    port:8018
}
  • 比如app-second/vite.config.js 设置:
 
server: {
    port:8020
}
  • app-first项目vite.config.js配置后的代码

juejin7.png

8、workspace的根目录安装依赖

pnpm i

9、根目录package.json中配置运行workspace里面每一个项目的命令

"scripts": {
    "dev:first": "pnpm run -C packages/app-first dev",
    "dev:second": "pnpm run -C packages/app-second dev",
    "build:first": "pnpm run -C packages/app-first build",
    "build:second": "pnpm run -C packages/app-second build"
  },

10、在workspace根目录启动项目

  • 运行的命令都配置好啦,你是不是以为现在就可以直接运行了呢?漏漏漏,如果直接运行你看看是啥效果:

juejin8.png

  • 那我们应该怎么办呢?这里记好了啊(敲黑板!!!),要再安装一遍依赖
pnpm i

juejin9.png

  • 然后再运行项目

juejin10.png

  • 另一个项目也可以直接运行了

juejin11.png

11、配置: 一个命令启动所有项目

  • workspace的 package.json 文件中
  • 我们已经为各个子项目(如app-first, app-second )配置了单独的开发(dev)和构建(build)脚本
  • 如果想要一次性启动所有子项目的开发服务器
  • 可以使用一个能够并行运行多个任务的工具
  • 比如 npm-run-all 或 concurrently
  • 这里我将展示如何使用 concurrently 来实现需求。
11.1、需要安装 concurrently:
npm install --save-dev concurrently
11.2、然后,在 package.json 中添加一个新的脚本来启动所有子项目:
"scripts": {
    "dev:all": "concurrently \"pnpm run -C packages/app-first dev\" \"pnpm run -C packages/app-second dev\""}
  • 配置后的scripts

juejin12.png

  • 注意,这里我们使用了双引号来包围每个 pnpm run 命令,并且整个 concurrently 命令也被双引号包围。这是因为在 JSON 中,字符串内部的特殊字符(如空格)需要被转义,而使用双引号是最简单的方法。此外,由于 Windows 命令提示符对引号的处理有所不同,这种方法在 Unix/Linux/macOS 和 Windows 的 Git Bash 或 PowerShell 中都应该能正常工作。
11.3、运行所有项目
pnpm dev:all
  • 结果:

junjin13.png

全局公共样式

  • 当多个项目都需要一样的样式,那么我们可以把这个样式抽离出来,定义在全局,定义一次,所有项目都可以直接使用

1、在pnpm-workspace.yaml配置公共样式

  • 比如:common

juejin13.png

2、在 workspace 根目录下创建这个common公共样式目录,并且在common目录下创建common-styles目录,common-styles目录下创建index.css文件

  • index.css文件中写你要的样式,例:
/* index.css */
/* 这里配置公共的样式,建议取名的时候前面用统一的前缀,方便查找 */
.common-button {
  background-color: #6b9cd0;
  color: white;
  border: none;
  padding: 10px 20px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  margin: 4px 2px;
  cursor: pointer;
}

.common-button:hover {
  background-color: #0056b3;
}
  • 文件结构:

juejin14.png

3、初始化 common-styles 包

  • 1.打开终端或命令行工具。
  • 2.导航到 common/common-styles 文件夹。
  • 3.运行 npm init -y 或 yarn init -y 来初始化一个新的 package.json文件。
pnpm init

juejin15.png

  • 得到的项目结构:

juejin16.png

4、在项目的package.json中添加依赖

  • 打开app-first和app-second项目里的package.json文件

  • 在 dependencies 部分添加对 common-styles 的依赖。由于 common-styles 是一个本地包,需要使用文件路径来指定它。

  • 例如,在 packages/app-first/package.json 和 packages/app-second/package.json 中添加:

"dependencies": { 
// 其他依赖... 
"common-styles": "file:../../common/common-styles" 
}

juejin17.png 注意:这里的路径是相对路径,它指向项目根目录下的 common/common-styles 文件夹。如果你的项目结构不同,请调整路径以匹配你的实际情况。

5、安装依赖

  • 1.回到 app-first 和 app-second 文件夹。
  • 2.运行 pnpm install 来安装依赖,包括刚刚添加的 common-styles。

juejin18.png

6、在项目中引入样式

  • 1.打开 app-first 和 app-second 中的主文件(例如 main.js 或 App.vue)。
  • 2.使用 import 语句来引入 common-styles 包中的 index.css 文件。
  • 例如,在 packages/app-first/main.js 和 packages/app-second/main.js 中添加:
import 'common-styles/index.css';

juejin19.png

注意:由于使用的相对路径依赖,并且没有将 common-styles 发布到 npm 仓库,因此不需要(也不能)在 node_modules 中找到它。Vite 或你的构建工具应该能够解析这个本地依赖并正确地引入样式文件。

7、使用全局样式

  • 现在,已经成功创建了一个 common-styles 包,并在 app-first 和 app-second 项目中引入了它的样式。common-styles的公共样式已经在项目中全局可用了。
  • 在项目中任何 Vue 组件或 HTML 文件中,只要给元素添加 common-buttonr类,它就会应用公共样式文件里面的样式:

juejin20.png

  • 比如我们现在要在app-first 和 app-second两个项目中都使用.common-button样式
  • 例:在app-first的vue.app页面使用公共样式

juejin22.png

  • 当项目运行时,这个元素就会显示在 common-styles/index.css 中定义的样式。
  • 效果:

juejin21.png

  • 在其他项目内也是这样直接使用类名即可拥有公共样式中定义的样式

全局公共组件

  • 在 workspace 的根目录下创建公共组件,并使这些组件能够在 workspace/packages/ 下的app-first 和 app-second两个项目以及未来可能添加的其他项目中复用,可以按照以下步骤操作:

1、创建公共组件目录

  • 例:在 workspace 根目录下创建 components(名字可以自定义)文件夹:
  • components下面可以写多个组件,比如建一个按钮组件MyButton,MyButton文件夹中创建一个.vue文件:
<!-- workspace/components/MyButton/MyButton.vue -->
<template>
  <button class="my-button">{{ label }}</button>
</template>

<script>
export default {
  name: 'MyButton',
  props: {
    label: {
      type: String,
      default: '这是定义在全局的MyButton组件'
    }
  }
}
</script>

<style scoped>
.my-button {
  background-color: rgb(228, 13, 13);
  color: white;
  border: none;
  padding: 10px 20px;
  cursor: pointer;
}

</style>

juejin23.png

2、配置构建系统

  • 在要使用的项目的vite.config.js 中配置路径别名(这里需要导入fileURLToPath和URL ):
  • 例:要在app-first项目中使用公共组件,那么就在app-first的vite.config.js中配置
import { fileURLToPath, URL } from 'node:url'

//名字随便取,路径根据你的项目结构配置
'@Mycomponents': fileURLToPath(new URL('../../components', import.meta.url)),

juejin26.png

3、使用公共组件

  • 例:在app-first的App.vue中使用:
//导入
<script setup>
import MyButton from '@Mycomponents/MyButton/MyButton.vue';
</script>

//结构中使用
<template>
 <MyButton></MyButton>
</template>
  • App.vue的内容:

juejin29.png

  • 重新运行项目即可出效果(如果运行不成功,先使用pnpm install 安装依赖,再运行)

  • 效果:

juejin30.png

结语:

通过以上步骤,你已经成功使用 pnpm workspace 实现了多项目的高效管理。每个项目都可以独立运行,又能共享资源,极大地提高了开发效率。现在,你可以轻松应对多个项目的开发和维护。

如果你有任何问题或建议,欢迎留言或私信交流哦!😊

官方文档