使用 lerna 管理项目中的业务组件
Opened this issue · 0 comments
方案的起源
单仓库管理多个包
在进行业务开发的时候,往往能抽离出很多相似的模块跟组件。自己负责的项目一般也不止一个,如果只是将其手动复制到不同的项目当中,组件跟模块往后的更新与迭代将会很困难。
大多数人都会这么干,那就是将公共的组件、模块、工具方法、发布到 npm私有仓库上进行统一的管理跟迭代...
遇到的问题
- 由于大家都是高自我要求的前端开发者,踊跃的抽离项目中的公共模块,发布到
npm
仓库当中,所以就造成了npm
公共组件仓库体积的急剧增加。 - 不仅如此,由于公共方法也被放到这个仓库当中,造成了一个很尴尬的情况,自己只是修改了公共方法当中的导出操作,不得不发布了整个仓库。
提出的解决方案(多仓库管理多个包)
最开始的解决方案,将公共操作方法,公共组件,公共模块弄成了三个仓库单独发布
自从这样实行以来,上面的问题得到了初步的解决,本以为满心欢喜,结果却遇到了更棘手的情况。
遇到的问题
- 在使用公共操作方法时,细心的小明发现了公共操作方法当中的某个缺陷,提出了改进的意见,于是提了一个
pull request
,负责管理公共仓库的小华,review
以后觉得相当的不错,于是打上了tag
更新了版本。 - 某日,小明在使用公共模块时,发现自己改进的操作方法,在这个模块当中没有更改,于是在查看源码之后,发现公共模块依赖的公共操作方法的版本没有升级...
简单的总结
- 使用单独的仓库进行管理多个包,更改一个,必须发布整个仓库,文件目录过多,阅读体验差。
- 使用多仓库管理多个包,难以梳理其中的依赖情况,不便于管理,维护多个仓库浪费时间。
那能不能一个仓库管理多个包,但是这些包相对于这个仓库,又是独立的呢?
答案是可以的,在调研之后,发现业界早已存在解决方案,那就是 monorepo
管理方案,本文基于monorepo
方案的开源框架 lerna
进行简单的讲解。
lerna
的简单使用
Lerna
是一个管理多个 npm 模块的工具,是 Babel 自己用来维护自己的 Monorepo 并开源出的一个项目。优化维护多包的工作流,解决多个包互相依赖,且发布需要手动维护多个包的问题。
简单的安装
npm i -g lerna
git init lernaExample
cd lernaExample
lerna init
然后根据提示,项目当中生成的 package.json
跟 lerna.json
如下
// package.json
{
"name": "root",
"private": true, // 私有的,不会被发布,是管理整个项目,与要发布到npm的解耦
"devDependencies": {
"lerna": "^3.15.0"
}
}
// lerna.json
{
"packages": [
"packages/*"
],
"version": "0.0.0" // 仓库的版本
}
创建 npm
包
这里有个注意点是,lerna
中的仓库名 @lernaExample
必须一致
lerna create @lernaExample/utils
lerna create @lernaExample/components
lerna create @lernaExample/decorator
文件目录
增加依赖
lerna add react // 为所有 package 增加 react 模块 (像这种公共的包可以使用 lerna bootstrap --hoist 提升到项目的工程目录)
lerna add classnames--scope @lernaExample/components // 为 @lernaExample/components 增加 classnames 模块
lerna add @lernaExample/utils--scope @lernaExample/components // 增加内部模块之间的依赖
// 想删除某个依赖项
lerna clean
发布
lerna publish
// 按照提示发布就行
// 如果发布报错,可能是没有相对于仓库的权限
lerna
完整示例
在上文中,只是简单的了解 lerna
的基本使用,在项目当中使用的话还存在下面的几个问题。
- 包中的文件没有经过编译,如果是直接书写的
es6
文件,则很有可能浏览器中不能兼容 - 没有对文件进行基本的
eslint
格式校验 - 没有对发布进行约束,比如
commit
校验,changlog
文件生成等
环境搭建
babel
环境
需要下载的 babel
包
"dependencies": {
"@babel/runtime": "^7.6.2",
"@lernaExample/utils": "^0.0.0",
"classnames": "^2.2.5",
"react": "^16.8.0"
},
"devDependencies": {
"@babel/cli": "^7.6.0",
"@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-decorators": "^7.6.0",
"@babel/plugin-transform-regenerator": "^7.4.5",
"@babel/plugin-transform-runtime": "^7.6.0",
"@babel/preset-env": "^7.6.0",
"@babel/preset-react": "^7.0.0",
"babel-plugin-import": "^1.12.2"
},
.babelrc
文件配置
{
"presets": [
[
"@babel/preset-env",
{
"modules": false // 模块使用 es modules ,不使用 commonJS 规范
}
],
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
["@babel/plugin-transform-regenerator"],
["@babel/plugin-proposal-class-properties"],
[
"@babel/plugin-transform-runtime",
{
"corejs": false, // 默认值,可以不写
"helpers": true, // 默认,可以不写
"regenerator": true, // 通过 preset-env 已经使用了全局的 regeneratorRuntime, 不再需要 transform-runtime 提供的 不污染全局的 regeneratorRuntime
"useESModules": true // 使用 es modules helpers, 减少 commonJS 语法代码
}
],
[
"import",
{
"libraryName": "antd"
}
] // 通过手动引入antd样式
]
}
package.json
文件中增加编译命令
"main": "lib/index.js", // 文件入口
"scripts": {
"compile": "babel src --out-dir lib",
...
},
使用方式 npm run compile
eslint
环境
我这边直接使用的 vscode
扩展的 eslint
工具,如果团队内部对 eslint
有自己的定义,可以单独下载 eslint
包,对其文件进行配置。
commit
以及 changelog
环境
自动校验commit规范
使用 husky
包,相关依赖项
"devDependencies": {
"@commitlint/cli": "^7.0.0",
"@commitlint/config-conventional": "^7.0.1",
"husky": "^0.14.3",
}
创建 commitlint.config.js
文件
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'bug',
'docs',
'style',
'refactor',
'test',
'chore',
'version',
'build',
'ci',
'perf',
'revert',
]
],
'scope-case': [2, 'always', ['lower-case', 'snake-case', 'upper-case', 'kebab-case']],
'subject-case': [0]
}
};
自动生成changelog
记录
npm i cz-lerna-changelog
下载 lerna
专用的 changelog
包
使用
- 更改了文件之后,执行
npm run compile
- 写上
commit
内容,如果内容不符合,会自动打回 - 如果不用发布版本,则直接执行
lerna version
,打上tag
生成changlog
记录 - 需要发布版本
- 只发布一个,可以先执行
lerna updated
,然后执行lerna publish
- 全部发布, 执行
lerna publish
- 只发布一个,可以先执行
- 如果不想生成版本记录,以及不发布版本,则直接执行
git push
总结
- 以往的仓库管理方式的不足之处
- 使用
lerna
之后,package
之间存在互相的依赖项,lerna
会自动对其管理,不需要手动更新package.json
中依赖项的版本号。 - 一个基本完整的
lerna
使用介绍