前言

Vue3 选用 monorepo 的方式进行项目代码办理。在本文中,能够学习到

  • monorepo 是什么,有哪些优缺点?了解其技能运用背景、处理技能痛点
  • 讨论几种完成 monorepo 战略的详细计划?依据项目场景挑选适合的技能计划
  • Vue3 源代码 monrepo 完成,了解其完成细节和目录标准规划。
  • monorepo 在大型项目有哪些新的技能计划

monorepo 是什么?

Monorepo(单一代码库房)是一种代码办理战略,用于将多个相关项目存储在同一个代码库房中。比较于传统的多个独立代码库房,Monorepo 的方针是进步代码的可同享性、可重用性和协作功率

那么,采纳一种新的战略,肯定是因为该战略具有一些长处。从下面这张图中,能够看出,项目代码的办理战略是在实践中不断发展变化的。

Vue3 源码阅览:monorepo 代码办理计划

  • 第一阶段 monolith:一开端不管多少代码都放在一个项目中进行办理,跟着时刻推移,代码量越来越多,每一次构建都会花费很长时刻,代码耦合度强,可保护性差,代码冲突频频等各种问题逐渐显现且愈加严峻。
  • 第二阶段 multi repo:将事务相对独立的功用拆分不同的项目进行保护,这样确实处理了一些问题,比方项目自治,可保护性变强。不过也存在一些问题,例如代码不能同享,联调困难,每个项目都要重复装置,版别办理等问题
  • 第三阶段 monorepo:因为存在以上种种问题,聪明的工程师想出的一种代码办理战略,接下来就剖析 monorepo 有什么优势和下风。

monorepo 优下风?

经过 monorepo 战略办理的代码,目录结构看起来会是下面这样,将不同项意图目录聚集到一个目录之下

.
├── package.json
└── packages/ # 这儿将寄存一切子 repo 目录
    ├── project_1/
    │   ├── index.js
    │   ├── node_modules/
    │   └── package.json
    ├── project_2/
    │   ├── index.js
    │   ├── node_module/
    │   └── package.json
    ...

monorepo 优势

monorepo 在代码办理上优势:

  1. 代码重用将变得简略:因为一切的项目代码都集中于一个代码库房,很简略抽离出各个项目共用的事务组件或东西,在代码内引证;
  2. 依靠办理将变得简略:因为项目之间的引证途径内化在同一个库房之中,简略追寻当某个项意图代码修改后,会影响到其他哪些项目。经过运用 lerna 一些东西,能够做到版别依靠办理和版别号主动晋级;
  3. 统一构建和测验:运用统一的构建装备和流程,削减装备和保护的作业量。此外,能够在整个 Monorepo 中履行统一的测验流程,确保一切项目质量和稳定性。
  4. 便于协作和开发:在一个代码库房中,更简略地浏览、查找和理解整个项意图代码,便于团队成员之间的协作。Monorepo 还能够促进跨项意图合作和常识同享,进步团队的整体功率和协同才能。
  5. 更少的内存:多个项目引证相同的依靠,只需求装置一份依靠即可,削减重复装置节约内存空间

monorepo 下风

其实,优势和下风都是相对的,在必定程度上,如果不遵从束缚和标准,优势也会转换为下风,所以在规划上要更加谨慎,这也是学习源码优秀规划的原因之一。

  1. 新员工的学习本钱变高:不同于一个项目一个代码库房这种形式下,在 monorepo 战略下,新人或许不得不花更多精力来理清各个代码库房之间的相互逻辑,当然这个本钱能够经过新人文档的方式来处理,但编写和保护文档也需求精力本钱;
  2. 团队协作和权限办理变复杂:在 Monorepo 中,团队成员需求同享同一个代码库房,而且对一切模块都具有相同的权限级别。这或许会导致一些团队成员对整个项意图代码和资源具有过多的拜访权限,增加了潜在的安全风险。
  3. 代码耦合和影响范围:在 Monorepo 中,一个模块的更改或许会对其他模块发生意外的影响,增加了代码耦合性,并或许导致意外的副作用。

怎么取舍?

看了之后是不是在犹豫要不要运用 monorepo 办理代码了,别这么早下定论,软件开发范畴从来没有完美一说,需求依据安排和特定的项目来挑选。能够把 monorepo 战略实践在「项目」这个级别,即从逻辑上确定项目与项目之间的相关性,然后把相相关的项目整合在同一个库房下。

通常情况下,咱们不会有太多相互相关的项目,运用 monorepo 技能能够办理多而乱的项目,完成项目复用,好好运用扩大它的长处,一起经过制定标准、项目文档办理标准补齐它的短板。

monorepo 完成计划

重新着重一下,monorepo 它是一个战略,是一种思维,而不是一个详细的东西,不要将它和 lenrnyarn workspace 划上等号,完成这个战略能够有多种计划,那么介绍以下3种计划。

lerna

Lerna 是为 monorepo 而生的东西,在项目中装备 lerna

1、项目根目录装置 lenrnanpm install lerna -D,运用 npx lerna init 初始化,于是根目录新增一个 lerna.json 文件,默许内容为:

{
    packages:[
        "packages/*"
    ],
    // 默许 npm 省掉
    "npmClient": "npm"
}

lerna 默许作业目录是 packages,lerna 默许运用的是 npm,省掉了装备项 "npmClient": "npm"

2、在 packages 目录创立多个独立的子包,别离初始化 package.json 文件,如下

.
├── package.json
└── packages/ # 这儿将寄存一切子 repo 目录
    ├── project_1/
    │   ├── index.js
    │   ├── node_modules/
    │   └── package.json
    ├── project_2/
    │   ├── index.js
    │   ├── node_module/
    │   └── package.json
    ...

3、在根项目 package.json 装备 scripts 运转指令,然后履行 npm run bootstrap,装置 packages 各个子包依靠

{
    scripts:{
        'bootstrap':'lerna bootstrap'
    }
}

lerna 怎么作业

1、lerna 有两种作业形式:形式固定形式(Fixed)、独立形式(Independent),运用 version 关键字表明

{
  "packages": ["packages/*"],
  "npmClient": "npm",
  "version": "independent"
}
  • independent 独立形式:将每个子项意图版别号看作是相互独立的。当某个子项目代码更新后,运转 lerna publish 时,相关的子项目版别号不会主动晋级

  • Fixed 固定形式:相反,运用固定形式时,任一子项意图代码变化,都会导致一切子项意图版别号根据当时指定的版别号晋级。

lerna 常用指令

Lerna 供给了很多 CLI 指令以满意咱们的各种需求,但依据 2/8 规律,应该首要关注以下这些指令

  • lerna run:会像履行一个 for 循环相同,在一切子项目中履行 npm script 脚本,而且,它会十分智能的辨认依靠关系,并从根依靠开端履行指令;
  • lerna exec:像 lerna run 相同,会依照依靠顺序履行指令,不同的是,它能够履行任何指令,例如 shell 脚本;
  • lerna publish:发布代码有变化的 package,因而首要需求在运用 Lerna 前运用 git commit 指令提交代码,好让 Lerna 有一个 baseline;
  • lerna add:将本地或长途的包作为依靠增加至当时的 monorepo 库房中,该指令让 Lerna 能够辨认并追寻包之间的依靠关系,因而十分重要;
  • --concurrency <number>:参数能够使 Lerna 运用核算机上的多个中心,并发运转,然后提高构建速度;
  • --scope '@mono/{pkg1,pkg2}':–scope 参数能够指定 Lerna 指令的运转环境,经过运用该参数,Lerna 将不再是一把梭的在一切库房中履行指令,而是能够精准地在咱们所指定的库房中履行指令,而且还支撑示例中的模版语法;
  • --stream:该参数可使咱们检查 Lerna 运转时的指令履行信息

yarn workspaces

yarn workspaces 天然自带 monorepo 才能。虽然没有专用的装备文件,但需求在项目根途径下 package.json 文件中做些装备,例如

{
    workspaces:[
        "packages/*"
    ]
}

履行 yarn install,各个子项目会装置各自的依靠项,装备相对简略

将 lerna + yarn workspace 结合完成 monorepo

在这儿 lernayarn workspace 人物清楚,依靠办理的作业交给 yarn worksapces,运用 lerna 供给的一些东西指令来优化对 monorepo 类型项意图办理,比方发动不同的项目,运用 lerna 挑选性的履行某些指令。一起lerna还供给了诸如版别发布等能够优化开发体会的东西

pnpm workspace

pnpm 作为一个比较新的东西,比较于 yarn 装置速度更快,占用内存更少,它也和 yarn 相同,供给了作业空间完成 monorepo

pnpm 装备 monorepo,在项目根目录下新建 pnpm-workspace.yaml 文件

  packages:
  - 'packages/*'

经过上面简略的装备,pnpm 就搭建了 monorepo 环境,完成起来适当简略。总的来说,小项目不需求 monorepo,在大项目中或许需求将事务和组件库代码抽离,需求考虑运用这种手法,完成多个项意图代码和装备同享

yarn 相同,pnpm 能够和 lerna 一起作业 。接下来介绍 Vue3 中 pnpm 完成 monorpo

Vue3 完成 monorepo

在 Vue3 项目 package.json 写明包办理器运用 pnpm,node 版别 18+,推荐全局装置 npm i -g pnpm

{
  "packageManager": "pnpm@8.15.0",
  "engines": {
    "node": ">=18.12.0"
  },
}

pnpm-workspace.yaml 装备文件告诉 pnpm 包办理目录是 packages

packages:
  - 'packages/*'

装置项目依靠 pnpm install,在根目录和 packages 子目录下别离装置依靠包,运转 pnpm dev 打包 vue 代码

能够看到 在 packages 目录有十几个项目,Vue3 将内部完成的部分抽象成了一个个模块,每个模块都有自己的类型声明、单元测验、构建测验流程, 打包独立 npm 发布,这样规划便于保护、发版和扩展。

一起,独立的子项目模块不仅仅能够在 vue3 中运用,例如 reactivity 响应式这个模块,装置 npm i @vue/reactivity 能够在 js、react 其他项目中运用

介绍下源码的目录规划

    core
    ├── packages             // vue 源码中心包,运用 pnpm workspace 作业区办理
    │   ├── compiler-core    
    │   ├── compiler-dom     
    │   ├── compiler-sfc     
    │   ├── compiler-ssr     
    │   ├── reactivity       
    │   ├── reactivity-transform  
    │   ├── runtime-core     
    │   ├── runtime-dom      
    │   ├── runtime-test    
    │   ├── server-renderer     
    │   ├── sfc-playground
    │   ├── shared             
    │   ├── size-check          
    │   ├── template-explorer  
    │   └── vue                 
    │   └── vue-compat          

包功用模块介绍:

  • compiler-core: 编译器(平台无关),例如基础的 baseCompile 编译模版文件, baseParse 生成AST
  • compiler-dom: 根据 compiler-core,专为浏览器的编译模块,能够看到它根据 baseCompilebaseParse,重写了complie、parse
  • compiler-sfc: 编译vue单文件组件
  • compiler-ssr: 服务端烘托相关的
  • reactivity: vue独立的响应式模块
  • runtime-core: 也是与平台无关的基础模块,有vue的各类API,虚拟dom的烘托器
  • runtime-dom: 针对浏览器的runtime。包括处理原生DOM API
  • runtime-test:一个专门为了测验而写的轻量级 runtime。因为这个 rumtime 「烘托」出的 DOM 树其实是一个 JS 目标,所以这个 runtime 能够用在一切 JS 环境里。你能够用它来测验烘托是否正确。
  • shared:内部东西库,不露出API
  • size-check:简略运用,用来测验代码体积
  • template-explorer:用于调试编译器输出的开发东西
  • vue :面向大众的完好版别, 包括运转时和编译器
  • api-extractor.json —— 一切包同享的装备文件。当咱们 src 下有多个文件时,打包后会生成多个声明文件。运用 @microsoft/api-extractor 这个库是为了把一切的 .d.ts 组成一个,而且,还是能够依据写的注释主动生成文档。
  • template-explorer: 用于调试编译器输出的开发东西。您能够运转npm run dev dev template-explorer并打开它 index.html 以获取根据当时源代码的模板编译的副本。在线编译网址:vue-next-template-explorer.netlify.app/#

Vue3 源码阅览:monorepo 代码办理计划

大型运用构建 Monorepo 计划

构建大型运用的计划有 TurborepoRushRushNx

Turborepo

Turborepo 是 Vercel 团队开源的高性能构建代码库房体系,答应开发者运用不同的构建体系。

构建加快思路:

  • Multiple Running Task:构建使命并行进行,构建顺序交给开发者装备
  • Cache、Remote Cache:经过缓存 及 长途缓存,削减构建时刻

Rush

  • 处理了幽灵依靠:将项目一切依靠都装置到 Repo根目录的 common/temp 下,经过软链接到各项目,确保了 node_modules 下依靠与 package.json 共同

  • 并行构建:Rush 支撑并行构建多个项目,进步了构建功率

  • 插件体系:Rush 供给了丰富的插件体系,能够扩展其功用,满意不同的需求,详细参考

  • 项目发布,ChangeLog 支撑友好:主动修改项目版别号,主动生成 ChangeLog

Nx

Nx 是 Nrwl 团队开发的,一起在保护 Lerna,现在 Nx 能够与 Learn 5.1及以上集成运用 构建加快思路(比 Turborepo 更丰富)

  • 缓存: 经过缓存 及 长途缓存,削减构建时刻(长途缓存:Nx 公开了一个公共 API,它答应您供给自己的长途缓存完成,Turborepo 有必要运用内置的长途缓存)
  • 增量构建: 最小范围构建,非全量构建
  • 并行构建: Nx 主动剖析项意图相关关系,对这些使命进行排序以最大化并行性
  • 分布式构建: 结合 Nx Cloud,您的使命将主动分布在 CI 署理中(多台长途构建机器),一起考虑构建顺序、最大化并行化和署理运用率

用 Nx 强壮的使命调度器加快 Lerna:Lerna 擅长办理依靠关系和发布,但扩展根据 Lerna 的 Monorepos 很快就会变得很痛苦,因为 Lerna 很慢。这就是 Nx 的闪光点,也是它能够真正加快你的 monorepo 的地方。

扩展阅览