物料市场-组件 monorepo 架构

背景

最近平台启动了物料市场技术项目,目的是通过提供丰富可复用物料、一体化物料解决方案、全链路研发来实现对研发体验和研发效率的提升。在设计公共组件架构时平台采用了多包架构(Monorepo),一套灵活的组件研发体系,并且天然支持按需使用。

什么是 Monorepo

monorepo 是一种将多个项目代码存储在一个仓库里的软件开发策略,这种方式在一个项目仓库(repo)中管理多个模块/包(package)。很多出名开源的项目都是采纳的 monorepo 的组织形式,比如 Babel,React ,Jest, create-react-app, react-router 、 npm@7 也带来一流的 monorepo 支持等等。

monorepo 的优劣

monorepo 的优势

  • 代码重用将变得非常容易
  • 依赖管理将变得非常简单
  • 代码重构将变得非常便捷

monorepo 的劣势

  • 项目粒度的权限管理变得非常复杂(既是优点也是缺点)
  • 学习成本变高
  • 库体积超大,目录结构复杂度上升
    基于两者的优缺点,结合我们当前组件库的特点:
    ● 每个包之间是有相关依赖的。
    ● 统一的构建工具,统一发版。
    ● 对版本的说明要求较高
    所以我们推荐采用 Monorepo 对组件库进行管理,目前最常见的 Monorepo 解决方案是 Lerna 和 Yarn 的 workspaces 特性。我们采用 Yarn 官方推荐的做法,用 Yarn 来处理依赖问题,用 Lerna 来处理发布问题。

lerna

A tool for managing JavaScript projects with multiple packages. Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.
基于上文我们可以知道 lerna 是最出名的 monorepo 的管理工具,也是当前项目采用的方案
通过 lerna 创建的项目结构

1
2
3
- packages(目录)
- lerna.json(配置文件)
- package.json(工程描述文件)

lerna.json 文件,默认内容为:

1
2
3
4
{
"packages": ["packages/*"],
"version": "0.0.0"
}

我们根据需求修改之后

1
2
3
4
5
6
{
"packages": ["packages/*"],
"npmClient": "yarn",
"version": "independent",
"useWorkspaces": true,
}
  • npmClient
    我们显示声明了我们的包客户端为 yarn。
  • useWorkspaces
    让 Lerna 追踪我们 workspaces 设置的目录。
  • version
    independent 将每个子项目的版本号看作是相互独立的。

Verdaccio

一个 npm 包本地发布工具,使用 Verdaccio 可以在本地创建一个 npm 仓库作为代理用来测试 lerna。

1
npm install --global verdaccio

在您的项目根目录创建 .npmrc 文件

1
registry="http://localhost:4873/"

每当您执行 lerna publish 时,子项目所构建成的 package 将会发布在本地 npm 仓库中,而当您执行 lerna bootstrap 时,Verdaccio 将会放行,让您成功从远程 npm 仓库中拉取相应的代码。

lerna 常用命令

  • lerna init
    初始化 lerna 项目
  • lerna create
    创建一个新的由 lerna 管理的包。
  • lerna add axios
    增加模块包到最外层的公共 node_modules 中
  • lerna add A –scope=B
    增加模块包到 packages 中指定项目,例如将 A 模块增加到 B 项目中
  • lerna list
    显示所有的安装的包
  • lerna clean
    从所有包中删除 node_modules 目录
  • lerna publish
    在当前项目中发布包 publish 不会发布 package.json 中 private 为 true 的包
  • lerna bootstrap
    lerna 提供了可以将子项目的依赖包提升到最顶层的方式 ,我们可以执行 lerna clean 先删除每个子项目的 node_modules , 然后执行命令 lerna bootstrop –hoist。
    基于上面的命令我们的脚手架 duwork 也同步实现

物料按照 Monorepo 的方式组织代码结构

包的结构

1
2
3
4
5
6
7
8
9
10
- packages
- buttonA 组件 A
- __test__ //存放测试相关代码
- dist // 打包的目录
- doc // 组件的文档
- src // 存放源码
- index.js // 打包的入口文件
- LICENSE // 版权信息(MIT)
- package.json // 组件的描述信息
- buttonB 组件 B

开发流程

1
2
3
4
5
6
创建
duwork create -c
打包
duwork build
发布
duwork publish

正常的开发流程是每个人新建一个 git branch,通过代码审核之后进行合并。从上面可以看到整套流程在 monorepo 架构下变得非常清晰。

总结

通过本文我们介绍了 monorepo,以及最佳实践,monorepo 给我们带来的收益是非常可观的,可能您的场景并不试用 monorepo,所以说脱离实际情况谈最优解都是不切实际的想法,一个模式的提出必定面对解决一个问题,但是即使您的场景并不试用 monorepo,还是希望工具和思想也可以运用到工作之中。

参考文章