因为公司项目独立npm包有点儿多,所以管理需要改变一下子项目结构,作筒一项目管理mono-repo
管理方式的尝试
一、几种代码管理方式
MonoLith: 一个项目,一个 Git 仓库。
- 优点:实现简单,一撸到底。
- 缺点:复杂项目代码复用性低,且不利于团队协作。
Multi-Repo: 划分为多个模块,一个模块一个 Git 仓库
- 优点:模块划分清晰,每个模块都是独立的 repo,利于团队协作
- 缺点:代码管理难度增加。比如:1.某个模块出现bug 相应模块都需要编译、上线、涉及到手动控制版本非常繁琐。 2.issue 管理十分麻烦。
Mono-Repo: 划分为多个模块,所有模块放在一个 Git 仓库
- 优点:代码结构清晰,利于团队协作,同时一个库降低了项目管理、代码管-理以及代码调试难度。
- 缺点:项目变得庞大,模块变多后同样会遇到各种问题。所以需要有更好的构建工具支持。
二、Lerna & Yarn
Lerna是一种工具,它优化了使用git和npm管理多包存储库的工作流。lerna 提供了近20个指令。在 MonoRepo项目中我们使用lerna 进行版本控制和包发布管理。
// 常用
lerna bootstrap // 安装所有依赖项并链接任何交叉依赖项
//例: lerna bootstrap --npm-client yarn --use-workspaces
lerna exec // 在每个包中执行任意命令
//例: lerna exec 'yarn remove lodash' // 删除
lerna add // 安装依赖,支持交叉依赖
// lerna add packageA --scope=packageB
// 版本发布
lerna changed // 检查自上次发布以来哪些软件包已经更新
lerna diff // 自上次发布以来,对所有包或单个包进行区分
lerna publish // 发布版本
// 常用
lerna clean // 清除项目中所有 node_modules
lerna init // 初始化项目
lerna create // 创建项目中的子package
// 其它
lerna run // 在包含该脚本的包中运行 npm 脚本
lerna info // 查看信息
lerna import // 导入
lerna link // 软链
lerna version // 查看版本
lerna ls // 列出当前 lerna 项目中的公共包
Lerna 的两种模式
- Fixed(固定模式):默认。所有package 共用一个版本号,比如:babel 任何 package 的 major change 均会导致所有包都会进行 major version的更新。
- Independent(独立模式):每个包都有自己独立的版本号。lerna会配合git,检查文件变动,只发布有改动的package。
Yarn
关于yarn workspaces,更详细的官方说明:
https://classic.yarnpkg.com/blog/2017/08/02/introducing-workspaces/
Yarn 是 facebook 开源的一个快速、可靠、安全的依赖管理工具。
在 MonoRepo项目中我们使用Yarn Workspaces 管理我们的依赖关系。 它没有多个node_modules目录,而是智能地优化了依赖关系的安装,并允许在monorepo中进行依赖关系的交叉链接。
// 常用
yarn install // 安装依赖项
yarn workspaces run clean // 清除项目中所有 node_modules
// yarn workspaces info
// yarn workspaces run
yarn add // 添加 package
yarn init // 初始化
yarn publish // 发布
yarn remove // 删除
yarn workspace // 具体某个工作区
// yarn workspace awesome-package add react react-dom --dev
三、一个例子
需要了解的工具和类库:
- Lerna:版本控制,包发布。
- Yarn workspaces:依赖管理。
- styled-components:是一个常用的 css in js 类库。
- Babel:编译下一代JavaScript 的编译器。
- Storybook:辅助UI组件开发的工具。
- Jest:JavaScript 测试框架。
安装 Lerna
npm install -g lerna
项目创建
git init mono-repos
cd mono-repos
yarn add -D lerna
lerna init
/package.json
{
"name": "root",
"private": true,
"devDependencies": {
"lerna": "^3.20.2"
}
}
/lerna.json
// 对 lerna 做简单修改
{
"packages": [
"packages/*"
],
"version": "independent", // 【独立模式】每个包都有自己独立的版本号
"npmClient": "yarn", // 执行命令的client,默认为npm,我们设置为 yarn
"useWorkspaces": true // 开启工作区模式 用 yarn 进行依赖管理
}
修改 /package.json ,设置 Yarn workspaces
{
"name": "root",
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"lerna": "^3.20.2"
}
}
babel
Babel 是一个 JavaScript 编译器。可以将es6+ 源码编译为 es5,让更多浏览器兼容。
yarn add --dev -W @babel/cli @babel/core @babel/preset-react @babel/preset-env babel-core@7.0.0-bridge.0 babel-loader babel-plugin-styled-components webpack
参数-W 指定 Yarn 为整个 workspace 安装指定依赖。 所有依赖为组件间共享组件。
/.gitignore
.log
.DS_Store
.jest-*
lib
node_modules
/babel.config.js
babel全局配置文件。
module.exports = {
plugins: ['babel-plugin-styled-components'],
presets: ['@babel/preset-env', '@babel/preset-react']
};
我们先用简单的方式创建编译脚本,后续引入 webpack 后集成到 webpack中。
/package.json
"scripts": {
"build": "lerna exec --parallel -- babel --root-mode upward src -d lib --ignore **/*.story.js,**/*.spec.js"
}
- lerna exec: 将接受任何命令并在所有 package 上运行。 此命令指示 Babel 将 /src 文件夹中的源码编译到/lib文件夹中。
- –parallel:在每个 package 上并行执行 - –root-mode upward:使用根目录中 babel.config.js 配置
- –ignore /*.story.js,/*.spec.js:忽略掉 .spec.js文件 以及 .story.js文件
/package.json
添加安装依赖脚本bootstrap指令:
"scripts": {
"bootstrap": "lerna bootstrap --use-workspaces"
}
四、jest 测试集成
创建测试环境,然后编写一个简单的测试用例。我们将使用 jest 进行单元测试,它会自动执行以.spec.js结尾的文件。
yarn add --dev -W jest jest-styled-components babel-jest react-test-renderer jest-resolve jest-haste-map
接下来,让我们在根目录中对 jest 进行配置。
/jest.config.js
module.exports = {
cacheDirectory: '.jest-cache',
coverageDirectory: '.jest-coverage',
coveragePathIgnorePatterns: ['<rootDir>/packages/(?:.+?)/lib/'],
coverageReporters: ['html', 'text'],
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100
}
},
testPathIgnorePatterns: ['<rootDir>/packages/(?:.+?)/lib/']
};
在 package.json 中添加测试脚本:
"scripts": {
"coverage": "jest --coverage",
"unit": "jest"
}
六、版本发布
在发布前,需要先提交源码到代码仓库。参考 GitHub 远程代码推送。
1、查看哪些 package 发生过更改
lerna changed
2、更改的具体内容
lerna diff
3、版本发布
Lerna publish
如果代码未提交,会有以下提示,需要先 提交代码。