创建设计系统单体仓库
我们正在建造什么?
Lerna是什么?
什么是单体仓库(monorepo)?
为什么要使用Lerna?
创建你的第一个单体仓库
文件夹结构
常用命令
其实没那么可怕👻
参考
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
在创建设计系统时,将功能拆分成更小、更独立的模块(或包)通常很有用。但如果您决定单独交付每个组件,或者将实用程序模块与组件一起交付,您就会陷入困境。
你会把它重构到一个独立的 Git 仓库和 NPM 包中吗?如何管理这些?比如共享构建/测试流程?如何创建一个可以轻松启动而无需过多维护的开发环境npm link?这就是 Lerna 的用武之地。
本文将介绍Lerna和monorepo 的概念,以及如何使用它们来创建和管理新的设计系统。您也可以直接跳到此处查看源代码。
我们正在建造什么?
以下是我们将要构建的内容以及我们将使用的技术的简要概述:
Lerna是什么?
Lerna 是一个用于管理包含多个包的 JavaScript 项目的工具。
我说的JavaScript项目,指的是一个单独的Git仓库。我说这个项目有多个包,指的是同一个Git仓库里有多个模块(比如NPM、Serverless Functions,甚至可能是Composer或其他语言的模块)。
什么是单体仓库(monorepo)?
这就是单体仓库(monorepo)。一个仓库可以存放多个项目/模块等等。
许多大型公司,例如Google和Facebook,以及像Gatsby或Vercel这样的小型公司,都使用单体仓库 (monorepo) 来帮助管理大型项目。以 Gatsby 为例,他们的单体仓库包含了 Gatsby CLI、入门模板,甚至还有插件——所有内容都位于同一个仓库中,确保你可以对任何特定的软件包进行本地修改。
为什么要使用Lerna?
当您想要创建一个单体仓库并更轻松地同时管理多个软件包时。
- 您可以将多个包链接在一起(例如,一个
core包可以被其他包使用——类似于 Yarn Workspaces 或其他类似方式npm link)。 - 将软件包及其提交历史记录导入到单体仓库中
- 一次性发布多个软件包并递增它们的软件包版本
通常情况下,无需使用 Lerna,只需利用相同的依赖项(例如 Yarn Workspaces)即可完成所有这些操作,但 Lerna 本身就提供了许多此类功能。这省去了编写常用脚本和工作流的时间。
创建你的第一个单体仓库
既然我们已经了解了 Lerna 和 monorepos 是什么,那就让我们实际启动一个吧!
- 为 monorepo 创建一个新文件夹:
mkdir your-monorepo && cd $_ - 运行 Lerna 安装程序:
npx lerna init
这将使用以下文件和结构引导您的项目:
lerna-repo/
packages/
package.json
lerna.json
- 要启用 Yarn 工作区(用于管理共享依赖项和链接包),请将以下内容添加到
package.json:
{
"name": "my-design-system",
"private": true,
"workspaces": ["packages/*"]
}
- 请确保程序
lerna.json能够从文件夹中拾取软件包/packages/,我们将把每个设计系统模块(或软件包)放置在该文件夹中:
{
"packages": ["packages/*"],
// Doesn't version the monorepo - keeps version to packages
"version": "independent",
"npmClient": "yarn",
"useWorkspaces": true
}
文件夹结构
完成lerna init上述流程和配置后,您应该使用类似于下面的文件夹结构:
在这个文件夹内,/packages你可以放置任何你想要用来拆分项目的包(也就是 NPM 模块)。例如,你可以创建 ` <site>`website和components`<components>` 文件夹,一个文件夹包含网站,另一个文件夹包含组件和设计系统。
所有包都应该位于同一文件夹层级。这意味着你不应该嵌套包(就像components/button嵌套文件夹一样)。/packages文件夹内的每个子文件夹都应该代表一个所需的 NPM 模块。
如果要嵌套模块,父文件夹应该只有一个模块,理想情况下,嵌套模块应该相互链接(从根目录下的模块链接/packages)。嵌套模块可以取消链接,但它们将无法在其他模块中使用(除非您链接父模块)。
请记住,您并非必须使用/packages文件夹或名称。如果您更改配置package.json,lerna.json则可以将 NPM 模块放在任何文件夹(或子文件夹)中。
常用命令
现在你已经有了你的第一个 monorepo,让我们来学习一些你会经常用到的 Lerna 常用命令。
首先,你肯定需要使用 Lerna 来创建新的软件包并将它们链接起来。然后,你可以通过对所有软件包运行命令或发布到 NPM(如果你喜欢的话,甚至可以使用传统的提交方式)来深入挖掘。
创建新包
- 进入 packages 文件夹:
cd packages - 为软件包创建一个文件夹,并导航到该文件夹:
mkdir your-package-name && cd $_ - 使用 npm 或 yarn 创建一个新项目:(
yarn init版本号从 0.0.0 开始——Lerna 会在首次发布时自动递增版本号)
如果您拥有支持私有包的 NPM 组织帐户,则可以将以下内容添加到模块的单独配置中package.json:
"publishConfig": {
"access": "restricted"
}
安装本地软件包作为依赖项
你经常会发现自己想要在设计系统的一个包中在另一个包中使用(例如core在你的包中使用一个包components,或者button在……中使用form)。
在这种情况下,Lerna 有一个命令lerna add可以处理将本地包链接到另一个包的过程(使用yarn link添加到package.json)。
- 进入Lerna项目根目录:
cd my-monorepo - 运行以下命令,将第一个软件包 添加到第二个软件包中:
lerna add button --scope=form
在这种情况下,form它将button作为依赖项。
您可以通过运行以下命令将软件包安装到monorepo 中的所有软件包lerna add package-name中。
对所有软件包运行命令
Lerna 提供了一个命令,可用于在每个包中运行相同的命令。运行lerna run test此命令后,将运行一个脚本,该脚本会遍历每个包并运行其中声明的测试脚本package.json。
lerna run test --stream
建议您创建一个单独的包,其中包含所有测试依赖项,并将其添加到所有其他模块中,这样可以将依赖项隔离到一个位置,从而更容易管理所有包中的测试。
- 理想情况下,您可以将测试设置在某个
utils软件包中testing。 - 将包添加
testing到所有组件(用于lerna add一次性添加到多个包) - 为每个组件添加
test脚本package.json - 使用以下方式运行测试
lerna run test
移除所有软件包的依赖关系
如果你在某个软件包中添加了不再需要的依赖项,Lerna 提供了一个命令,可以从 Yarn 工作区(以及共享依赖项)中删除它们。
- 前往 Lerna 项目根目录
- 运行以下命令:
lerna exec -- yarn remove dep-name
发布所有软件包
当您需要将软件包发布到 NPM 时,Lerna 可以帮您完成这个过程。登录后,您可以运行 Lerna 的发布命令,创建一个包含所有已更新软件包的新版本,同时也会更新 NPM 上的所有软件包。
- 登录 NPM:
npm login - 运行发布脚本:
lerna publish
版本分离和发布
如果出于某种原因,您需要完全控制版本控制,Lerna 可以将版本控制和发布拆分为两个命令。如果您希望更好地控制版本和发布,这将非常有用。
您可以手动运行:
lerna version
然后按照提示更新各个版本号。
然后你可以编写一个发布脚本,读取最新的标签(手动更新过的标签)并将其发布到 NPM:
lerna publish from-git --yes
配置
自动常规提交
Lerna 支持使用 常规提交标准 在 CI 环境中实现语义版本控制的自动化。
这使得开发者能够提交如下消息:
git commit -m "fix: JIRA-1234 Fixed minor bug in foo"
然后,在持续集成 (CI) 环境中,可以根据类似上述的提交更新软件包版本并将其发布到 NPM。这可以通过配置 CI 环境来运行以下命令:
lerna publish --conventional-commits --yes
如果您不想在每次发布时都传递标志,请将以下内容添加到您的 lerna.json 文件中。
lerna.json:
"command": {
"publish": {
"conventionalCommits": true,
"yes": true
}
}
强制执行常规承诺
使用常规提交是一回事,但真正强制执行这些提交又是另一回事。
强制执行这些规则意味着使用提交检查流程来解析所有提交,并确保它们符合常规的提交标准。
由于记住提交的确切语法可能很麻烦,因此强烈建议您设置一个提交 CLI 流程(例如 commitzen),引导开发人员完成常规提交,并确保每次都符合规范。
这个过程比较复杂,所以我会在另一篇文章中详细介绍。
其实没那么可怕👻
希望这篇文章能帮助大家揭开单体仓库的神秘面纱,了解如何创建和使用它们。单体仓库是一种高效的工作流程,有助于简化和扩展开发。无论在设计系统领域内外,学习单体仓库都是一项非常实用的技能,因为大多数大型项目最终都会采用单体仓库结构。
我使用这种工作流程创建了一个包含多个 Storybook 的单体仓库,这样我就可以拥有一个类似“设计系统笔记本”的东西。这样,我就可以记录任何前端框架(例如 React 或 Vue)的笔记,并将所有笔记集中在一个地方。
还有什么我遗漏的,你还是不明白的吗?请在评论区或推特上告诉我,我会尽力解答👍
请务必查看我的 Instagram 帖子,里面有您可以保存以备后用的快速参考资料:
参考
示例
- https://github.com/whoisryosuke/design-systems-monorepo
- 创建基础设计系统的示例项目。
- https://github.com/whoisryosuke/design-systems-notebook
- 使用单体仓库创建多个 Storybook(React、Vue 等)


