npm、yarn、pnpm实现monorepo并使用changset管理版本
在前端开发领域中有很多库和框架他都是使用的monorepo
架构实现的,它的好处有很多,可以很方便的管理自己的目录结构,还可以实现分包的功能等。比如在vue3
中就使用到了这个架构,它会将runtime
阶段和complier
阶段和响应式阶段进行更细致的划分。当我们需要什么功能就可以引入单独的包来实现。
像这种多包的概念,我们每次更新都要去手动更改package.json
中的版本,这样会很麻烦。可以使用changeset
来管理我们每个包的版本。接下来我们就来实现一下。
mkdir monorepo-npm-yarn-pnpm
cd monorepo-npm-yarn-pnpm
我有这样一个场景,我有一个logger
方法就是用来记录函数调用日志或者接口日志等等的一些日志信息的。我们用monorepo
架构的方式来实现一下,这种场景确实不需要monorepo
架构,但是为了掩饰使用我们就用这一个工具吧。
npm monorepo
先来看npm
如何实现monorepo
。
mkdir monorepo-npm
cd monorepo-npm
npm init -y
npm install typescript @types/node --save-dev // 安装typescript和node类型提示
npx tsc --init // 初始化tsconfig
修改tsconfig.json
修改成如下内容
{
"compilerOptions": {
"outDir": "dist",
"types": [ "node" ],
"target": "es2016",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
}
}
然后创建两个包
npm init -w packages/core -y
npm init -w packages/logger -y
可以看到两个包已经创建完成,package.json
也自动加了workspaces
配置项。
我们需要在core
中添加logger
的依赖
npm install logger --workspace core
可以看到core
的依赖中引入了logger
的包并且在node_modules
中创建了looger
的软链接。
改下包的名称,然后install
一下。
接下来我们初始化子包的tsconfig
文件。这里我们需要使用npm exec --workspace 子包名称 -- 命令
这样的命令来找到子包并运行命令。
npm exec --workspace @spring-npm/core -- npx tsc --init
npm exec --workspace @spring-npm/logger -- npx tsc --init
然后修改tsconfig.json
文件和上面的一样就行。
接下来我们来写logger
日志的方法。
这里需要使用chalk
第三方工具库来为我们的日志信息添加颜色。当我们需要为子包安装包的时候需要使用命令npm install --workspace 包名 库名
npm install --workspace @spring-npm/logger chalk
首先在package.json
中添加type: 'module'
的配置,让nodejs
支持esmodule
的模块规范。
这个loggerFun
方法就根据日志类型打印不同的颜色的日志信息,非常简单。信息包含了函数调用的时间、函数名称以及函数的返回值。
import chalk from "chalk";
export enum LogLevel {
DEBUG,
INFO,
WARN,
ERROR,
}
export function loggerFun<T>(type: LogLevel, funName: string, message: string, reutrnValue: T = {} as T): void {
const time = new Date().toLocaleTimeString();
let resultValue: string = "";
if (Object.prototype.toString.call(reutrnValue) == "[object Object]") {
resultValue = JSON.stringify(reutrnValue);
} else if (Object.prototype.toString.call(reutrnValue) == "[object Array]") {
resultValue = "[" + (reutrnValue as Array<any>).join(", ") + "]";
} else {
resultValue = reutrnValue + "";
}
if (type === LogLevel.DEBUG) {
debugLogger(funName, message, resultValue, time);
} else if (type === LogLevel.INFO) {
infoLogger(funName, message, resultValue, time);
} else if (type === LogLevel.WARN) {
warnLogger(funName, message, resultValue, time);
} else if (type === LogLevel.ERROR) {
errorLogger(funName, message, resultValue, time);
} else {
console.log(chalk.whiteBright(`[] ${time} - ${funName} - returned: ${reutrnValue}`));
}
}
function debugLogger(funName: string, message: string, reutrnValue: string, time: string) {
console.log(chalk.blueBright(`[DEBUG] ${time} - ${funName} - ${message} - returned: ${reutrnValue}`));
}
function infoLogger(funName: string, message: string, reutrnValue: string, time: string) {
console.log(chalk.greenBright(`[INFO] ${time} - ${funName} - ${message} - returned: ${reutrnValue}`));
}
function warnLogger(funName: string, message: string, reutrnValue: string, time: string) {
console.log(chalk.yellowBright(`[WARN] ${time} - ${funName} - ${message} - returned: ${reutrnValue}`));
}
function errorLogger(funName: string, message: string, reutrnValue: string, time: string) {
console.log(chalk.redBright(`[ERROR] ${time} - ${funName} - ${message} - returned: ${reutrnValue}`));
}
接下来我们对这个方法进行tsc
编译成js
文件,然后修改成package.json
将入口指向变异后的文件。
npm exec --workspace @spring-npm/logger -- npx tsc
修改package.json
文
这里logger
包就完成了。
接下来写core
的代码。
import { loggerFun, LogLevel } from "@spring-npm/logger";
function add(a: number | string, b: number | string): number {
loggerFun(LogLevel.DEBUG, add.name, "进入函数");
if (typeof a !== "number" || typeof b !== "number") {
loggerFun(LogLevel.ERROR, add.name, "参数错误");
throw new Error("参数错误");
}
loggerFun(LogLevel.INFO, add.name, "完成", a + b);
return a + b;
}
add(1, 2);
add(1, '2');
changeset 版本管理
到这里monorepo
的架构就完成了,功能也实现了。接下来我们结合changeset
来进行版本管理。
登陆npm,然后添加一个组织
因为我们是@spring-npm
开头的包,所以我们就叫spring-npm
。
接下来将core
和logger
包在package.json
添加属性,都改成公有的
"publishConfig": {
"access": "public"
}
执行npm adduser
登陆npm
。
npm adduser
如果你npm弹出来的是中文页面,那么就需要将淘宝镜像改成npm的镜像。我是使用nrm
来改的。
nrm use npm
这样就登陆成功了。
然后安装changeset
npm install --save-dev @changesets/cli
初始化changeset
,会生产一个.changeset
目录,里面就是记录你的版本信息的。
npx changeset init
因为changeset
是基于git代码提交变动的,所以我们要初始化git仓库。
git init
git add .
git commit -m 'first commit'
然后执行npx changeset add
,创建一次changeset记录
npx changeset add
这里我们选择更改小版本minor
。
然后npx changset version
来修改版本
npx changeset version
可以看到版本全部修改,修改的是小版本。
然后提交git,并发布到npm上
git add .
git commit -m 'version 1.1.0'
npx changeset publish
这里就发布完成。
然后将core
和logger
的index.ts
都加个回车,我们修改大版本试一下。
然后上面的步骤都是一样的。
这里升级大版本也完成了。这里就是npm + monorepo + changeset就完成了。
yarn
yarn 和 pnpm 其实都是差不多的,只有命令不同。这里就不带着大家一一去写了。只有执行子包的命令和安装子包的第三方库有些命令上的不同。其它都是一样的,使用changeset也是一样的。
yarn 初始化两个目录
npm init -w packages/logger -y
npm init -w packages/core -y
yarn core引用logger包
yarn workspace core add logger@1.0.0
yarn 为子包安装依赖
yarn workspace cli add chalk commander
yarn 执行子包命令
cd packages/core
npx tsc --init
pnpm
pnpm添加pnpm-workspace.yaml
文件,指定子包目录。
packages: - 'packages/*'
创建两个包
mkdir packages packages/core packages/logger
cd packages/core
npm init -y
cd ../logger
npm init -y
为core包添加logger的依赖
pnpm --filter logger add core --workspace
执行子包的命令
pnpm --filter logger exec npx tsc --init
子包添加依赖
pnpm --filter logger add chalk
这里pnpm为主包添加依赖需要使用-w
pnpm add --save-dev -w @changesets/cli
总结
pnpm monorepo会更好用一点,npm和yarn都是存在幽灵依赖的问题,同时npm的monorepo受版本的影响。pnpm安装包更快没有幽灵依赖的问题。
无论是npm、yarn还是pnpm它们的monorepo架构都是相同的,只有执行子包的命令不同。只需要知道执行子包的命令就行了。写法上都是通用了。大家有时间可以将yarn和pnpm都实现一遍。如果遇到什么问题可以评论区讨论一下。