最佳实践 monorepo + pnpm + vue3 + element-plus 0-1 完整教程

10,908 阅读5分钟

前言:

本文将教会大家如何快速搭建 monorepo + pnpm + vue3 + element-plus 项目架构

目标:即使初学者也可以跟随教程完整搭建 monorepo 架构

温馨提示:码字不易,先赞后看,养成习惯!!!

0:monorepo是什么?

维基百科:In version-control systems, a monorepo is a software-development strategy in which the code for a number of projects is stored in the same repository.

翻译:在版本控制系统中,monorepo 是一种软件开发策略,其中许多项目的代码存储在同一存储库中。

听着挺唬人,说白了就是多仓库项目统一在一个 git 仓库管理的这种管理模式叫做 monorepo

1:安装环境

1:pnpm: 8.1.0

2:node: 16.16.0

2:架构搭建

2.1:创建项目

新建一个文件夹命名为 monorepo 通过 vscode 打开,新建终端。

image.png

2.2:初始化项目根目录

image.png

在package.json里加一行 "private": true 加这个为防止我们意外地将私有项目发布

{ 
    "name": "monorepo",
    "version": "1.0.0",
    "description": "", 
    "main": "index.js", 
    "private": true, 
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"  
    }, 
    "keywords": [],
    "author": "",
    "license": "ISC"
}

2.3:创建根目录

按照 pnpm 官网指导在根目录创建 pnpm-workspace.yaml 文件

image.png

修改一下如图:

image.png

packages:  
  # 放置各种项目  
  - 'packages/*'  
  # 放置各种公用代码  
  - '_public_/**'

接下来创建对应的文件夹,如下图:

image.png

2.4:初始化目录

_public_ 文件夹下的模块初始化,添加一下 "private": truename 字段修改成自定义名称

image.png

同上步骤修改 utils

image.png

2.5:新建 index.js

components &  utils 目录下新建 index.js 文件

这里要注意,如果是用 ts 请创建 index.ts 文件

image.png

这时候需要思考一个问题,componentsutils 的定位。对于组件我们需要模板,而工具库我们是不需要的。所以需要对 components 进行 git 初始化,

image.png

在该目录下新建一个组件文件夹 components

image.png

执行命令 pnpm i vue element-plus

同时新建两个组件 button.vue & switch.vue 文件

image.png

在组件 button.vue & switch.vue 中写入以下代码

Button:

<script setup>  
  import 'element-plus/dist/index.css'  
  import { ElButton } from 'element-plus'  
  import { add } from 'utils'  
</script>  
      
<template>  
    <div>  
        <el-button @click="add(1,2)">我是 ElButton</el-button>  
    </div>  
</template>  

<style scoped>  
</style>

Switch:

<script setup>  
  import 'element-plus/dist/index.css'  
  import { ElSwitch } from 'element-plus'  
  import { ref } from 'vue'  
  const value1 = ref(true)  
</script>

<template>  
    <div>  
        <el-switch v-model="value1" />  
    </div>  
</template> 

<style scoped>  
</style>

utils 里新建一个工具函数 fun.js

image.png

代码如下

// 调用弹框提示  
export const windowAlert = (str) => {  
    window.alert(str)  
}  
export const add = (a, b) => {  
    window.alert(`相加的结果为:${a+b}`)  
}

index.js 中引入 fun.js

export * from './fun.js'

image.png

index.js 中导出两个组件

import Button from './components/button.vue'  
import Switch from './components/switch.vue'  
export {  
    Button,  
    Switch  
}

image.png

2.6:创建子项目

packages 里创建项目 demo1

执行 pnpm create vite 然后按照提示选择自己想要创建的项目(如何创建?更多细节指导?请看这里

image.png

同理创建 demo2

image.png

2.7:安装公用组件、工具

执行命令: pnpm i akubela-components akubela-utils

image.png

可以看到创建的 akubela-components & akubela-utils 两个组件包已经安装到 demo1 中。同理将 demo2 也安装一下

进入 demo1 项目,将 App.vue 文件修改一下,如下:

image.png

代码如下:

<script setup>  
import { windowAlert } from 'akubela-utils'  
import { Button, Switch } from 'akubela-components'

windowAlert('我是demo1')  
</script>  
<template>  
  <div>  
    <h1>我是demo1</h1>  
    <Button></Button>  
    <Switch />  
  </div>  
</template>

<style scoped>  
</style>

demo1 项目先跑起来试试,执行:pnpm dev

如下图报错了,原因是我们在在组件仓库中引入了另外一个仓库的代码,所以找不到该文件

image.png

如何解决,既然是项目组件那在 monorepo 下就可以当做包处理,我们直接安装到全局即可

回到根目录安装

执行命令:pnpm i akubela-components akubela-utils

image.png

报错了,看了一下报错信息就是告诉我们,如果要安装到根项目中(即全局项目中)那么可以在指令后面加上 -w

重新执行命令:pnpm i akubela-components akubela-utils -w

image.png

全局安装成功,再试试看能不能跑起来了

重新执行命令:pnpm dev

image.png

还是没找到,找了一下发现是安装包名搞错了,改一下 utils → akubela-utils

image.png

重新执行命令:pnpm dev

image.png

打开浏览器试试

1.gif

可以完美运行,试试 demo2

2.gif

2.8:当前实现了什么?

1:实现了多项目共用公共项目组件库

2:实现了公用项目组件库间的相互调用

3:实现项目全量启动

3:说明

可以看到 pnpm + monorepo + vue3 + element-plus 这个一个架构雏形就基本形成了,目前在这个架构下可以干什么?

1:支持多项目并存在一个仓库下,不限于原来项目所使用的架构技术方案等

2:可以将各项目进行分包管理

3:可以精简大量公用代码(项目级别)

4:打包

既然是全流程,那自然缺少不了构建打包流程,推荐使用 Turborepo

但如果不喜欢或者不习惯使用的也可以使用 pnpm 来进行此流程,也完全没有问题。

image.png

4.1:安装 Turborepo

执行命令: pnpm install turbo --global

使用 turbo -v 查看是否安装成功

image.png

安装成功后在根文件夹中加入 turbo.js 文件

image.png

默认代码:

{  
    "$schema": "<https://turbo.build/schema.json>",  
    "pipeline": {  
        "dev": {  
          "cache": false  
        },  
      "build": {  
        "dependsOn": ["^build"],  
        "outputs": [".next/**", "!.next/cache/**"]  
      },  
      "test": {  
        "dependsOn": ["build"],  
        "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]  
      },  
      "lint": {}  
    }  
  }

然后去根文件夹的 package.json 加入对应的 key 执行命令行

image.png

各项目中编辑 .gitignore 加入:

.turbo

build/

dist/

.next/

image.png

关于 Turborepo 更多细节

执行流:

1:根目录输入 pnpm dev 该目录下找到 dev 执行脚本执行→ 执行 turbo dev 命令 → 匹配每一个项目的对应的 dev 命令行,如果找到并发执行

2:根目录输入 pnpm build 该目录下找到 build 执行脚本执行→ 执行 turbo build 命令 → 匹配每一个项目的对应的 build 命令行,如果找到并发执行

3:根目录输入 pnpm build:demo1 该目录下找到 build:demo1 执行脚本执行→ 执行 turbo build --filter=demo1 命令 → 匹配到 demo1 项目,执行该项目下的 build 命令

4:根目录输入 pnpm build:demo2 同3执行逻辑

备注:filter 语法细则

运行 pnpm dev

3.gif

运行 pnpm build

4.gif

运行 pnpm build:demo1

5.gif

看来我们的各种命令执行都得到了我们想要的结果,输出了打包文件 dist

为了更为彻底的验证,最后一步将打包好的文件依次开启服务运行运行看看功能是否完整

6-1.gif

至此从 0-1 一步步完成了 monorepo + pnpm + vue3 + element-plus + turborepo 架构搭建完成。各位前端er是不是感觉也不难。有兴趣小伙伴完全可以基于此教程搭建输出一套企业级的统一仓库解决方案。也可以跟随教程自己搭建一个。

源码地址

5:总结

monorepo 是一个好的集成解决方案,我们也可称之为工程化的一种思路,我们所熟知的 Vue 就是采用了 monorepo 的仓库管理方案, 但万事不能全美,既然能一个仓库放 n 个项目带来便利的同时缺点也需要我们去关注思考。

1:代码权限管控让人头疼,不仅仅是项目组之间的,公司的全量代码也可以被任意开发人员拉走。

2:仓库代码量随时间推移代码量激增,拉取一次代码都显得力不从心。(拉取过几百兆的仓库就知道了我在说什么)

3:公用项目组件库一旦出问题,所有项目齐崩无一幸免。(公共组件库管理是一个具有挑战性的工作,里面的门道太多了)

4:对开发组的能力提出更高要求,因为你完全不知道会有人拿这些组件做什么谜一样的操作。

5:对于侵入性代码较为无力,一旦被意外入侵带来的问题可能超乎想象。

6:推荐好文

  1. 最佳实践 monorepo + pnpm + vue3 + element-plus 0-1 完整教程
  2. Vite+rollup项目如何大幅提升性能体验
  3. 面试官系列:请说说你对深拷贝、浅拷贝的理解
  4. 面试官系系列:说说前端异处理吧
  5. 面试官系列:请你说说原型、原型链相关
  6. 面试官系列:请手写防抖或节流函数debounce/throttle
  7. 面试官系类:请手写instanceof
  8. 10分钟快速手写实现:call/apply
  9. 5分钟快速手写实现:bind
  10. 5分钟快速手写实现:new
  11. 10分钟入门SVG:SVG如何画出单身狗?来本文告诉你

7:参考

en.wikipedia.org/wiki/Monore…

cn.vitejs.dev/guide/#scaf…

turbo.build/repo/docs/g…

juejin.cn/post/712926…

juejin.cn/post/714618…

pnpm.io/zh/pnpm-wor…