关于 npm 管理参考:一文讲透 npm 管理 monorepo
什么是 Monorepo
Monorepo(单一仓库)是一种代码管理策略,它指的是在一个单一的版本控制仓库中维护多个项目或包。这些项目可能彼此独立,或者相互依赖,比如共享代码库、库组件、服务应用程序、前端应用等。
管理方案
项目的管理方案采用 pnpm 管理依赖项,其他操作优先考虑使用 npm -- 取其精华,舍弃其糟粕。
初始化仓库
-
创建项目目录 monorepo-demo 并初始化
package.json:# Linux / MacOS / Windows mkdir monorepo-demo cd monorepo-demo npm init在此步中填写正确的包名(可加入命名空间),例如 @demo/root,可忽略第五步中的操作。
-
创建基于
pnpm管理工作区的配置文件:# Linux / MacOS touch pnpm-workspace.yaml # Windows type nul >pnpm-workspace.yaml加入下面内容,代表子项目存放在 @demo 目录中:
packages: - '@demo/*' -
初始化子项目 a 与 b:
# Linux / MacOS mkdir -p @demo/{a,b} cd @demo/a && npm init cd ../b && npm init # Windows mkdir @demo\a @demo\b cd @demo\a & npm init cd ..\b & npm init在此步中填写正确的包名称(可加入命名空间),例如 @demo/a,可忽略第五步中的操作。
当前目录结构:
. ├── @demo │ ├── a │ │ └── package.json │ └── b │ └── package.json ├── package.json └── pnpm-workspace.yaml -
由于根项目不需要发布,所以设置为私有化(等同于在根项目的
package.json中加入private="true"):npm pkg set private="true" -
(可选)设置根项目与子项目名称:
npm pkg set name="@demo/root" npm pkg set name="@demo/a" --prefix @demo/a npm pkg set name="@demo/b" --prefix @demo/b
依赖安装
安装全局(公共)依赖
lodash 依赖:
# --workspace-root 可简写为 -w
pnpm add --workspace-root lodash
基于子项目安装依赖
axios 依赖,两种方式:
-
基于工作区的安装方式:
pnpm add --filter @demo/a axios -
进入子项目目录安装:
# Linux / MacOS cd @demo/a && pnpm add axios # Windows cd @demo\a & pnpm add axios
基于工作区互相依赖
-
子项目 b 引用子项目 a:
pnpm add @demo/a --filter @demo/b -
子项目 a 作为全局(公共)依赖:
pnpm add @demo/a --workspace-root
编写测试代码
@demo/a
- 在子项目 a 中创建文件(或手动在 @demo/a 下创建两个文件:
index.js,answerer.js):
# Linux / MacOS
touch @demo/a/{index.js,answerer.js}
# Windows
type nul > @demo\a\index.js
type nul > @demo\a\answerer.js
-
index.js代码:console.log("Hello, @demo/a"); -
answerer.js代码:const axios = require("axios"); module.exports.getAnswer = () => { axios({ method: "get", url: "https://yesno.wtf/api", }).then(({ data }) => console.log(JSON.stringify(data))); };
@demo/b
-
在子项目 b 中创建文件(或手动在 @demo/b 下创建一个文件:
index.js):# Linux / MacOS touch @demo/b/index.js # Windows type nul > @demo\b\index.js -
index.js代码:const a = require("@demo/a/answerer"); a.getAnswer();
当前目录结构
- 项目全局安装
lodash; - 基于子项目 a 安装
axios; - 子项目 a 作为子项目 b 的依赖;
.
├── @demo
│ ├── a
│ │ ├── answerer.js
│ │ ├── index.js
│ │ ├── node_modules
│ │ │ └── axios ⇒ ../../../node_modules/.pnpm/axios@1.6.2/node_modules/axios
│ │ └── package.json
│ └── b
│ ├── index.js
│ ├── node_modules
│ │ └── @demo
│ │ └── a ⇒ ../../../a
│ └── package.json
├── node_modules
│ └── lodash ⇒ .pnpm/lodash@4.17.21/node_modules/lodash
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
配置运行脚本
-
为子项目添加运行脚本(等同于在子项目的
package.json中加入start="node index.js"的运行脚本):npm pkg set scripts.start="node index.js" --prefix @demo/a npm pkg set scripts.start="node index.js" --prefix @demo/b -
在根项目添加运行脚本,关联子项目运行脚本(等同于在根项目的
package.json中加入两个scripts的运行脚本):npm pkg set scripts.start:a="npm run start --prefix @demo/a" npm pkg set scripts.start:b="npm run start --prefix @demo/b"
运行
npm 运行:
npm run start:a # 运行子项目 a,显示 Hello, @demo/a
npm run start:b # 运行子项目 b,显示通过 a 请求的接口数据
npm run start:all # 同时运行两个子项目
npm run --prefix @demo/a start # 直接运行子项目 a 的脚本命令
npm run --prefix @demo/b start # 直接运行子项目 a 的脚本命令
pnpm 运行:
pnpm start:a # 通过根项目运行子项目 a 的脚本命令
pnpm start:b # 通过根项目运行子项目 b 的脚本命令
pnpm --filter @demo/a start # 直接运行子项目 a 的脚本命令
pnpm --filter @demo/b start # 直接运行子项目 b 的脚本命令
区别:默认情况下,pnpm 不会任意执行用户定义脚本的 pre 钩子和 post 钩子(例如 prestart)。 这种从 npm 继承过来的习惯,会导致脚本执行是隐式的,而不是显式的,从而混淆了执行流程。
本文为原创内容,版权所有 © 2024 姚生。欢迎转载,但请注明出处,并附上原文链接。未经授权不得用于商业用途。如有疑问,请联系作者。