使用 node 管理器管理 monorepo

news2024/11/15 14:00:02

使用 node 管理器管理 monorepo

不包含工具的使用,一方面因为我没用到过工具,另外一方面看了一下 Lerna,说 Learna 底层还是用到了 yarn 去进行管理,二者并不冲突,所以打算先学习一下基础再说。

顾名思义,monorepo 指的就是 mono(一个)repository,即使用一个 repo 去管理整个项目,也就是代码(code)和资源(assets),其结构大概为:

|- repo
|-  |- app-a
|-  |- app-b
|-  |- lib-a
|-  |- lib-b

顺便,如果使用的是 symlink,有一个好处就在于修改 lib-b 中的代码,是不需要重新进行打包的,其他的 package 可以自动获取最新的代码。

构建一个 monorepo

这里虽然会带一点使用 npm 去运行 monorepo 的方法,不过主要还是用 yarn。

基础结构为:

|- packages
|  |- module-a
|  |  |- index.js
|  |  |- package.json
|  |- module-b
|  |  |- index.js
|  |  |- package.json

模块中的 package.json 可以使用默认初始化进行生成,即 npm init -y,index.js 中的代码就是一段 log 输出 a 或者 b。

暴力方法

即将 packages 的名称改为 node_modules,node 这样会自动寻找 node_modules 目录下的包并运行,如:

在这里插入图片描述

当然这个方法不是一个长期有效的方法,尤其是项目会涉及到第三方库时,管理就会变得非常的麻烦。

毕竟大多数情况下,node_modules 中的内容都会被 .gitignore 所忽略,一个 workaround 是重写 .gitignore 文件,指定不需要被 git 忽略的文件,不过依旧,在涉及到使用一些第三方库的前提下,单独在 .gitignore 中列举文件很快就会变得难以管理。

symlink

这一部分代码不变,指令如下:

➜  monorepo git:(main)cd packages
➜  packages git:(main)cd module-b
➜  module-b git:(main)npm link

added 1 package, and audited 3 packages in 458ms

found 0 vulnerabilities

➜  module-b git:(main)cd ../module-a
➜  module-a git:(main)npm link module-b

added 2 packages, and audited 5 packages in 408ms

found 0 vulnerabilities
➜  module-a git:(main)node index.js
b
a
➜  module-a git:(main)

link 指令 npm 和 yarn 通用,这里会将当前的 package 注册为 global module,随后切换到 module-a 中去进行对应的注册,这样就可以在 module-a 中使用 module-b 了。

在这里插入图片描述

重新跑了一遍,之前有报错,有点懒得打码了

如果项目需求比较简单,这也不失为一个短期的解决方法。

workspaces

三大管理器其实都有使用 workspace,但是 yarn 出来的最早,所以这里以 yarn 优先说明。

使用 workspaces 最大的优势在于管理器可以自动进行 symlink,省去了手动管理的麻烦。同时 npm 也会尽可能地使用 作用域升(hoisting),使得项目之间可以共用同样的 dependencies,从而提升性能。

此时项目变动如下:

|- packages
|  |- module-a
|  |  |- index.js
|  |  |- package.json
|  |- module-b
|  |  |- index.js
|  |  |- package.json
|  |- package.json

模块之间的代码没有任何变化,

基础配置

package.json 更新内容如下

{
  "name": "monorepo",
  "workspaces": {
    "packages": ["packages/*"]
  },
  "private": true
}

"packages/*" 是对 packages 下面进行了模糊匹配,一个个列举出来也是可以的。

关于 private 这个变量,官方文档其实有说明:

Worktrees used to be required to be private (ie list "private": true in their package.json). This requirement got removed with the 2.0 release in order to help standalone projects to progressively adopt workspaces (for example by listing their documentation website as a separate workspace).

如果 yarn 的版本为 v2 以上则可以不用添加,运行结果:

➜  monorepo git:(main)yarn --version
1.22.19
➜  monorepo git:(main)yarn
yarn install v1.22.19
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
✨  Done in 0.10s.

作用域提升

这个是使用 workspace 的优势之一,如 a 和 b 同时都会使用 相同的包,与其在每个模块中安装对应的 lodash,yarn/npm 会将下载的依赖包放到项目顶部的 node_modules 中,如:

➜  monorepo git:(main)cd packages/module-a
➜  module-a git:(main)yarn add lodash
yarn add v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
info All dependencies
└─ lodash@4.17.21
✨  Done in 1.97s.
➜  module-a git:(main)cd ../module-b
➜  module-b git:(main)yarn add lodash
yarn add v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Saved 1 new dependency.
info Direct dependencies
info All dependencies
└─ lodash@4.17.21
✨  Done in 0.44s.

效果如下:

在这里插入图片描述

如果二者装的依赖版本不一样,那么其中一个项目会将该依赖安装与当前项目中的 node_modules 中:

➜  module-b git:(main)yarn add lodash@3
yarn add v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
info All dependencies
└─ lodash@3.10.1
✨  Done in 4.50s.

在这里插入图片描述

如果想要在当前项目中禁止 hoisting,也可以直接在 package.json 中修改:

{
  "name": "monorepo",
  "workspaces": {
    "packages": ["packages/*"],
    "nohoist": []
  }
}

一般来说默认不会被 hoist 的项目是一些会与二进制打交道的项目,比如说 react-native、electron 之类的跨平台实现。。

运行脚本

这是一个比较少见的 usecase,不过也肯定会存在。

这里的假设就是,module-b 的作用类似于 cli,需要将整体代码打包让其他的项目去运行这个模块。

这里首先修改 module-b 的 index.js:

#!/usr/bin/env node
// 上面这段代码告知 npm/yarn 下面代码需要直接用 node 去执行

console.log('module b is running');

随后更新 package.json:

{
  "name": "module-b",
  "bin": "./index.js"
}

随后在 terminal 重新安装一下,更新 symlink:

➜  packages git:(main)yarn install --force
yarn install v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Rebuilding all packages...
success Saved lockfile.
✨  Done in 0.21s.

这里必须要用 --force 去强行执行一下更新,否则 yarn 会因为 dependency 没有变动而忽略掉 bin 中的内容。

最后就可以在直接脚本中运行 module-b,如:

{
  "name": "monorepo",
  "workspaces": {
    "packages": ["packages/*"],
    "nohoist": []
  },
  "private": true,
  "scripts": {
    "start": "module-b"
  }
}
➜  packages git:(main)yarn start
yarn run v1.22.19
$ module-b
module b is running
✨  Done in 1.05s.

如果要重命名 module-b 导出的模块,可以将 bin 中的内容修改成对象:

{
  "name": "module-b",
  "bin": {
    "mdb": "./index.js"
  }
}

重新运行一下 yarn install --force 后,就可以以 "scripts": { "start": "mdb" } 的语法去运行 module-b 了。

同时,因为这个脚本是在全剧的 node_modules 下进行的 symlink,因此该项目下所有的包都可以找到并运行这个脚本。

workspace 指令

刚才运行的指令其实都有在切换一些 repo,但是 yarn 有提供 workspace 指令,可以直接在某一个目录中使用 yarn workspace module command 去进行操作,如:

# 一直在根目录 monorepo 下
➜  monorepo git:(main)yarn workspace module-a add lodash-contrib
yarn workspace v1.22.19
yarn add v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
info All dependencies
└─ lodash-contrib@4.1200.1
✨  Done in 2.36s.
✨  Done in 3.30s.
➜
➜  monorepo git:(main)yarn workspace module-a start
yarn workspace v1.22.19
yarn run v1.22.19
$ mdb
module b is running
✨  Done in 1.00s.
✨  Done in 1.90s.
➜
➜  monorepo git:(main)yarn workspace module-b build
yarn workspace v1.22.19
yarn run v1.22.19
$ node ./index.js
module b is running
✨  Done in 0.90s.
✨  Done in 1.75s.

其他管理器

大部分的使用是通的,这里会列举一些细微的差别。

npm

需要注意的是,在 npm v7 以前,这个功能是不支持的,可以使用 yarn。

npm v6 应该可以粗略对标为 node v14,我看了下我本地的 14 用的是 6.14.12,node v16 用的是 8.19.2,目前最新的 npm 版本在 v9,应该对标的是 node v18?

想要具体查看当前的 node version 还是需要使用指令查看:

➜  module-a git:(main) ✗ nvm use 14
Now using node v14.16.1 (npm v6.14.12)
➜  module-a git:(main)npm --version
6.14.12
➜  module-a git:(main) ✗ nvm use 16
Now using node v16.17.1 (npm v8.19.2)
➜  module-a git:(main)npm --version
8.19.2

根目录下的 package.json 省去其他可以不需要的自动生成属性后如下:

{
  "name": "monorepo",
  "workspaces": ["packages/module-a", "packages/module-b"]
}

随后在根目录下运行 npm i:

➜  monorepo git:(main)npm i

added 2 packages, and audited 5 packages in 531ms

found 0 vulnerabilities

⚠️:虽然官方文档上用的是数组,不过我直接用 yarn 的 package.json,以对象的方式也是可以运行的。

workspace 指令的细微差异

使用 npm 的话,workspaceworkspaces 是作为一个 flag 存在的:

➜  monorepo git:(main)npm --workspace=module-a run start

> module-a@1.0.0 start
> mdb

module b is running

相对而言会比 yarn 要麻烦一些。

不过 npm 可以在同时运行所有名称相同的脚本,如:

➜  monorepo git:(main)npm --workspaces run start

> module-a@1.0.0 start
> mdb

module b is running
npm ERR! Lifecycle script `start` failed with error:
npm ERR! Error: Missing script: "start"

Did you mean one of these?
    npm star # Mark your favorite packages
    npm stars # View packages marked as favorites

To see a list of scripts, run:
  npm run
npm ERR!   in workspace: module-b@1.0.0
npm ERR!   at location:

为了预防这种错误,npm 也提供了一个 flag 去跳过不存在的指令:

➜  monorepo git:(main)npm --workspaces --if-present  run start

> module-a@1.0.0 start
> mdb

module b is running

注意指令上的细微不同,单独运行一个 module 用的是 workspace,运行所有的指令使用的是 workspaces

⚠️:目前 npm 不支持使用 nohoist,虽然出现版本冲突的处理方式依旧是一个会 hoist,另一个不会。

pnpm

yarn 和 npm 之间的配置还挺像的,但是 pnpm 的话会要求在 monorepo 的根目录下创建一个 pnpm-workspace.yaml 的文件:

packages:
  - packages/*

第二步是要检查版本,现在 a 中引用了 b,但是 pnpm 不像 npm/yarn 那样直接做 symlink,而是通过 virtual store 去进行版本管理,这个时候就要手动维护一下版本的一致性,下面就是 a 中的 package.json:

{
  "name": "module-a",
  "dependencies": {
    "module-b": "workspace:*"
  }
}

最后,pnpm 没有 workspace 这个 flag,而是使用 --filter 这个 flag:

➜  monorepo git:(main)pnpm i
Scope: all 3 workspace projects
Packages: +4
++++
Packages are hard linked from the content-addressable store to the virtual store.
  Content-addressable store is at: /Users/Library/pnpm/store/v3
  Virtual store is at:             node_modules/.pnpm
Progress: resolved 4, reused 4, downloaded 0, added 4, done
➜
➜  monorepo git:(main)pnpm -F "module-a" run start

> module-a@1.0.0 start /Users/study/monorepo/packages/module-a
> mdb

module b is running

pnpm 的优点在于,如果该脚本不存在,那么 pnpm 会直接忽略而不是报错。另外,因为 pnpm 使用的是 virtual store,因此也不存在 hoisting。

参考

  • npm workspaces

  • yarn Workspaces

  • pnpm Workspace

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/443001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

800V高压系统的驱动力和系统架构分析——为什么是800V高压系统,及其挑战?

摘要: 800V高压系统下汽车系统架构会出现哪些变化? 过去一年是新能源汽车市场爆发的一年,据中汽协数据,2021年新能源汽车销售352万辆,同比大幅增长157.5%。新能源汽车技术发展迅速,畅销车辆在动力性能、智…

IS210AEBIH3BED包含逻辑集成电路、存储器集成电路、专用集成电路

IS210AEBIH3BED包含逻辑集成电路、存储器集成电路、专用集成电路 什么是集成电路测试仪   集成电路测试仪是对集成电路进行测试的专用仪器设备。集成电路测试是保证集成电路性能、质量的关键手段之一。集成电路测试技术是发展集成电路产业的三大支撑技术之一,因此…

ELK部署

ELK部署 1. 整体部署规划1.1 服务器规划1.2 关闭防火墙,同步时间 2. ElasticSearch集群部署2.1 环境准备2.2 部署 Elasticsearch 软件 3. ELK Logstash 部署3.1 安装Logstash,httpd,java3.2 测试 Logstash与elasticsearch功能是否能做对接3.3 定义 logstash配置文件…

ES-IK分词器的概念和基本使用

文章目录 一、ES-IK分词器1.1 初识ES-IK分词器1.2 IK分词器-拓展和停用1.3 索引库1.3.1 mapping属性1.3.2 索引库的CRUD基本语法: 1.3.3 文档的DSL 一、ES-IK分词器 1.1 初识ES-IK分词器 ES IK分词器是一种基于中文文本的分词器,它是Elasticsearch中文分…

DJ4-4 网际协议:因特网中的转发和编址

目录 一、因特网中的网络层协议 二、IP 数据报格式(IPv4) 三、IP数据报分片和重组 1. 分片的概述 2. 分片的例子 四、IP 地址 1. IP 地址概述 2. IPv4 编址 3. IP 地址结构 4. 传统的 IP 地址分类 5. ABC 类地址 6. 特殊 IP 地址段 7. 特殊…

Mysql 截取字符串并将文本转换为数值

有一个需求, 需要在 字符串 20230410 中获取 月份(04), 然后变为 (4), 解决: SELECT cast(left(SUBSTRING(20230410, 5),2) as SIGNED); 用到的函数有 left(str, length) substring(str, pos),即:substring(被截取字符串, 从第几位开始截…

Linux驱动之在Ubuntu下编译驱动模块——学习笔记(12)

为了方便驱动开发学习,了解一下在Ubuntu上进行驱动编译的流程。 一、下载对应的内核源码 首先要通过 uname -a查询一下自己的内核版本。 我这里下载的是 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.4.tar.gz 二、编译内核 (1&a…

一起学 WebGL:复合矩阵

大家好,我是前端西瓜哥。之前讲了平移矩阵、旋转矩阵以及缩放矩阵,以及演示了在 WebGL 中的单独应用的效果。 这次我们看看同时进行多次矩阵变换的组合写法。 我们将会对一个三角形先平移,然后旋转。 矩阵乘法 简单过一下矩阵乘法的知识点…

Cassandra windos安装

首先从http://cassandra.apache.org/download/网站上找到cassandra,如下图所示: 2、点击3.11.3跳转到下载地址,如下图所示: …

DHCP服务器 DNS服务器 综合实验

配置DNS服务器 DNS服务器 主域服务器host1 192.168.80.101 从域服务器host2 192.168.80.102 DHCP服务器 host1 192.168.80.101 提供192.168.80.0/24网段 IP地址、默认网关、子网掩码、DNS服务器地址、DNS默认域名 客户端 Linux host3 19…

数据可视化神器!Matplotlib Python教程 | 从入门到精通绘制各种类型的图形和保存图形

大家好,我是爱吃熊掌的鱼,今天我要给大家带来一篇有趣开朗的Matplotlib Python教程。Matplotlib是Python中最流行的数据可视化库之一,它可以帮助我们将数据转化为易于理解的图表和图形。无论你是初学者还是专业人士,Matplotlib都是…

Eureka 注册中心——服务的注册与发现

文章目录 1.Eureka的结构和作用2.搭建eureka-server2.1.创建eureka-server服务2.2.引入eureka依赖2.3.编写启动类2.4.编写配置文件2.5.启动服务 3.服务注册3.1.引入依赖3.2 配置文件3.3启动多个user-service实例 4.服务发现4.1引入依赖4.2 配置文件4.3 服务拉取和负载均衡 最后…

flowable流程图绘制工具flowable-ui的安装和使用

一.简介 记录绘制flowable图的过程。 二.下载安装 1.下载 我这边是windows的,下载的版本是6.8.0,zip那个就可以,tar.gz是linux的 下载地址:https://github.com/flowable/flowable-engine/releases/tag/flowable-6.8.0 下载截…

PyCharm-2023安装教程

访问JetBrains的官方网站,下载PyCharm最新版本的安装程序。 双击下载的安装程序,在弹出的安装向导中点击“下一步”。 阅读许可协议,并同意协议条款。 选择安装路径。默认情况下,PyCharm会安装在C:\Program Files\JetBrain…

STL——Lambad表达式

📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段>——目标Windows,MySQL,Qt,数据结构与算法,Linux,多线程&…

【C语言】输入输出、字符串操作、内存操作、文件操作函数

三对基本输入输出函数 1.gets(),puts() gets()从标准输入中获取一个字符串,到str(自己创建的char型数组)中,读到换行或输入末尾结束获取r; 成功返回str,失败返回空。 char *gets(char *str)…

ARM汇编第一次上机(顺序、分支、单重循环)【嵌入式系统】

ARM汇编第一次上机(顺序、分支、单重循环)【嵌入式系统】 前言推荐说明ARM汇编第一次上机(顺序、分支、单重循环)内容1 sum1流程图代码编写结果分析 2 sum2流程图代码编写结果分析 3 numbers流程图代码编写结果分析 最后 前言 20…

Python统计学:配对样本t检验~均值差的检验

配对样本 t检验 用于两个不同条件下,对同一对象进行测量所得的两个样本均值的区别。 比如吃药前和吃药后体温是否有变化,大鱼钩和小鱼钩钓的鱼的体长是否有差异等。 配对样本是什么? 配对样本需要满足两个基本条件: 1. 样本间存…

Java版本工程管理系统软件源码 自主研发,工程行业适用

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下: 首页 工作台:待办工作、消息通知、预警信息,点击可进入相应的列表 项目进度图表:选择(总体或单个)项目显示…

【Java 数据结构】PriorityQueue(堆)的使用及源码分析

🎉🎉🎉点进来你就是我的人了 博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔🦾&am…