背景
我们在管理项目常遇到这些问题:
- 新建项目需要重新配置各种工具,繁琐,虽然也可以用脚手架生成,但后期涉及到依赖的升级也麻烦
- babel、eslint-config、stylelint等配置希望可以多项目共享。
其实为了解决上述问题,我们可以项目内使用** lerna + yarn workspaces**
Monorepo 和 Multirepo
multirepo
multirepo也就是我们常见的做法,也就是每一个package都单独用一个仓库去管理。例如,Webpack, babel…
这样可以多元化发展(各项目都有自己的构建工具,依赖管理策略,单元测试)。
但是多个npm导致复用的时候就要关心版本号,不好找出问题。
monorepo
monorepo是把所有相关package都放在一个仓库里进行集中管理,减少项目间的差异带来的各种成本。
所以我们开发的时候把一些可以共用的配置,比如eslint,babel,rollup等,可以统一的管理这些开发配
,开发者只需要关心业务代码,不需要关心复用方式
monorepo的项目结构,一般都会配合 yarn workspace 来管理包的依赖。
yarn workspaces – 管理包
使用yarn workspaces的项目,根目录我们叫它workspace root(工作区根目录)。
如何开启?
- 在项目根目录的 package.json 中添加这段代码
// 项目提交到 github 或发布到 npm 的时候,禁止提交当前根目录的内容。
// 当组件库开发完毕后,要发布到 NPM。工作区的根目录一般是脚手架,不需要发布。
// private 是为了防止意外把内容暴露出去。
"private": true,
// 存储要管理的所有包的路径
"workspaces": [
"packages/*"
]
复制代码
依赖管理
- workspace依赖 —— 也就是package与package之间互相引用
# lerna add <package>[@version] [--dev] [--exact]
npx lerna add xxx --scope yyy
复制代码
- package依赖 —— 具体某个包的依赖
# yarn workspace <workspace_name> [add xx][remove xx]
yarn workspace www add vue
复制代码
- 公共依赖 – 比如babel,eslint等工具依赖,需要共享配置
# 在根目录下进行
yarn add vue -D -W
复制代码
lerna – 统一发布项目中的所有包
Lerna 是一个管理多个 npm 模块的工具,是 Babel 自己用来维护自己的 Monorepo 并开源出的一个项目。优化维护多包的工作流,解决多个包互相依赖,且发布需要手动维护多个包的问题。
如果使用了Lerna,那么仓库结构会变成:
常用步骤
1. 安装
- 全局安装
npm i -g lerna
复制代码
- 项目内安装 – 在 package.json 中添加开发依赖(保证其他开发者使用时安装了lerna)
yarn add lerna --dev
复制代码
{
//...
"devDependencies": {
//...
"lerna": "^3.22.1",
}
}
复制代码
2. 创建项目
- 初始化
lerna init
复制代码
初始化后:
- 如果当前项目没有被 git 管理,会进行 git 初始化
- 在项目根目录创建一个 lerna.json 的配置文件
// lerna.json
{
// 所要管理的包的路径,默认是["packages/*"],不是从 package.json 获取的
// 如果根目录中没有 packages,就会自动创建该目录
"packages": [
"packages/*"
],
// 当前项目初始化版本
"version": "0.0.0"
}
复制代码
- 启动yarn workspaces
// 在lerna.json内增加如下内容以在lerna内启用yarn workspaces
{
"npmClient": "yarn"
"useWorkspaces": true
}
复制代码
3. 添加子包
环境初始化好,需要添加一个子包,命令
lerna create <pkgName>
复制代码
子包默认的目录结构如下:
4. 打版本
lerna的version_bump和changelog生成都依赖于conventional-commit,因此需要保证commit-msg符合规范。
version_bump
发版的时候需要更新版本号,这时候如何更新版本号就是个问题,好的办法是根据 git 的提交记录自动更新版本号,实际上只要我们的 git commit message 符合Conventional commit 规范,即可通过工具根据 git 提交记录,更新版本号。
简单的规则如下:
- 存在 feat 提交: 需要更新 minor 版本。
- 存在 fix 提交: 需要更新 major 版本。
- 存在BREAKING CHANGE提交: 需要更新大版本。
步骤:
- 添加@commitlint/cli和@commitlint/config-conventional和husky以及git-cz添加git-cz,支持commit-msg提示。
yarn add -W -D @commitlint/cli @commitlint/conventional-commit lint-staged husky commitizen cz-conventional-changelog
复制代码
- 配置commitlint
// commmitlint.config.js
module.exports = {
extends: [
"@commitlint/config-conventional"
]
};
复制代码
- 在package.json配置commit-msg的hooks
{
"scripts": {
"commit": "git-cz"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
复制代码
这样后续commit,就可以使用yarn commit进行commit,其会自动做出如下提示
其自动生成的commit-msg如下
5. 发布
- 修改lerna.json配置文件
{
...
"command": {
"publish": {
"ignoreChanges": ["*.md"],
"registry": "https://registry.npmjs.org/",
"verifyAccess": false, // 内网发包需开启
"verifyRegistry": false, // 内网发包需开启
"message": "chore: publish" // 修改默认的publish的commit msg
}
}
}
复制代码
- 配置发版的策略,我们积极convention-commit来发版
{
"scripts": {
// 生成changelog文件以及根据commit来进行版本变动,不提示用户输入版本
"versions": "lerna version --conventional-commits --yes"
},
"publishConfig": {
"access": "public"
}
}
复制代码
lerna publish from-git
复制代码
发布npm包是需要登录npm账号的,这里有个要注意的问题。
原因是没登录npm,其实原因很简单,想发布个东西总得让人家知道你是谁吧,好的,现在解决问题只需要两步。
- 切换到npm源(切记记得记得)
npm config set registry=https://registry.npmjs.org
复制代码
- 登录 / 添加用户
npm adduser/ npm login
复制代码
5. 维护依赖
当我们维护这个项目时,新拉下来仓库的代码后,需要为各个 package 安装依赖包。
当我们为某个packagea安装的包都放到了这个package目录下的node_modules。
这样如果多个package机会有多个node_modules。
我们使用 –hoistba把每个依赖包提升到跟目录,降低安装以及管理的成本。
lerna bootstrap --hoist
复制代码
也或者我们不想每次都输入–hoist参数,可以在lerna.json配置:
{
...
"command": {
"bootstrap": {
"hoist": true
}
},
...
}
复制代码
配置好后,对于之前依赖包已经被安装到各个 package 下的情况,我们只需要清理一下安装的依赖即可:
lerna clean
复制代码
执行 lerna bootstrap
即可看到 package 的依赖都被安装到根目录下的 node_modules 中了。