monorepo 包管理方式
最后更新于
这有帮助吗?
最后更新于
这有帮助吗?
monorepo 是一种项目架构。 简单来说: 一个仓库内包含多个开发项目(模块、包).
目前使用 lerna 进行多 packages 管理的明星仓库有:
babel/babel
facebook/create-react-app
vuejs/vue-cli
webpack/webpack-cli
multirepo 将应用按照模块分别在不同的仓库中进行管理
monorepo 将应用中所有的模块一股脑全部放在同一个项目中
各模块独立方便管理(对于 element 来说,修改表单只需要修改 packages 下的 form 目录)
结构清晰(模块独立之后,结构自然清晰)
有依赖的项目之间调试非常方便,上层应用能够感知其依赖的变化,可以很方便的对依赖项进行修改和调试。
缺点就是仓库代码体积可能比较大(一个仓库包含多个项目,项目多了,体积自然会大)
Workspaces 是设置包架构的一种新方式。他的目的是更方便的使用 monorepo, 具体就是能让你的多个项目集中在同一个仓库,并能够互相引用--被依赖的项目代码修改会实时反馈到依赖项目中。monorepo 中的子项目称为一个 workspace,多个 workspace 构成 Workspaces。
使用 Workspaces(以 yarn 为例)好处:
依赖包可以被 linked 到一起,这意味着你的工作区可以相互依赖,代码是实时更新的。这是比 yarn link
更好的方式因为这只会影响工作区部分,不会影响整个文件系统。
所有项目的依赖会被一起安装,这让 Yarn 更方便的优化安装依赖。
Yarn 只有一个 lock 文件,而不是每个子项目就有一个,这意味着更少的冲突。
使用 monorepo 策略后,收益最大的两点是:
避免重复安装包,因此减少了磁盘空间的占用,并降低了构建时间; 内部代码可以彼此相互引用; 这两项好处全部都可以由一个成熟的包管理工具来完成,对前端开发而言,即是 yarn(1.0 以上)或 npm(7.0 以上)通过名为 Workspaces 的特性实现的(⚠️ 注意,支持 Workspaces 特性的 npm 目前依旧不是 TLS 版本)。
为了实现前面提到的两点收益,您需要在代码中做三件事:
调整目录结构,将相互关联的项目放置在同一个目录,推荐命名为 packages;
在项目根目录里的 package.json 文件中,设置 Workspaces 属性,属性值为之前创建的目录;
同样,在 package.json 文件中,设置 private 属性为 true(为了避免我们误操作将仓库发布);
在项目根目录中执行 npm install 或 yarn install 后,您会发现在项目根目录中出现了 node_modules 目录,并且该目录不仅拥有所有子项目共用的 npm 包,还包含了我们的子项目。因此,我们可以在子项目中通过各种模块引入机制,像引入一般的 npm 模块一样引入其他子项目的代码
默认是 npm,每个子 package 下都有自己的 node_modules,如果使用 yarn workspace,可以共享 node_modules,减少安装时间
安装依赖包 使用 pnpm 安装依赖包一般分以下几种情况:
全局的公共依赖包,比如打包涉及到的 rollup、typescript 等
pnpm 提供了 -w, --workspace-root 参数,可以将依赖包安装到工程的根目录下,作为所有 package 的公共依赖。 比如:
如果是一个开发依赖的话,可以加上 -D 参数,表示这是一个开发依赖,会装到 pacakage.json 中的 devDependencies 中,比如:
给某个 package 单独安装指定依赖
pnpm 提供了 --filter 参数,可以用来对特定的 package 进行某些操作。 因此,如果想给 pkg1 安装一个依赖包,比如 axios,可以进行如下操作:
需要注意的是,--filter 参数跟着的是 package 下的 package.json 的 name 字段,并不是目录名。 关于 --filter 操作其实还是很丰富的,比如执行 pkg1 下的 scripts 脚本:
filter 后面除了可以指定具体的包名,还可以跟着匹配规则来指定对匹配上规则的包进行操作,比如:
此命令会执行所有 package 下的 build 命令。具体的用法可以参考 filter 文档。
模块之间的相互依赖
最后一种就是我们在开发时经常遇到的场景,比如 pkg1 中将 pkg2 作为依赖进行安装。 基于 pnpm 提供的 workspace:协议,可以方便的在 packages 内部进行互相引用。比如在 pkg1 中引用 pkg2:
此时我们查看 pkg1 的 package.json,可以看到 dependencies 字段中多了对 @codemao/monorepo2 的引用,以 workspace: 开头,后面跟着具体的版本号。
在设置依赖版本的时候推荐用 workspace:*,这样就可以保持依赖的版本是工作空间里最新版本,不需要每次手动更新依赖版本。 当 pnpm publish 的时候,会自动将 package.json 中的 workspace 修正为对应的版本号。
我们面临的问题:
如果我们需要在多个子目录执行相同的命令,我们需要手动进入各个目录,并执行命令;
当一个子项目更新后,我们只能手动追踪依赖该项目的其他子项目,并升级其版本。
Lerna 是一个工具,它优化了使用 git 和 npm 管理多包存储库的工作流。
将一个大的 package 拆分成多个小的 package,便于分享和调试
多个 git 仓库中更改容易变得混乱且难以跟踪
多个 git 仓库中维护测试繁琐
lerna 有两种模式让你去管理你的项目:固定(Fixed)或独立(Independent)。
Fixed/Locked 模式(默认)
固定模式。该模式为单版本号,在根目录中的 lerna.json 中设置。当使用 lerna publish 时,如果自从上次发布后有模块改动,那么将会更新到新发布的版本。
这也是目前 Babel 用的模式,当你想要自动整合不同包的版本时使用这个模式。它的特点是任何 package 的 major change 均会导致所有包都会进行 major version 的更新。
Independent 模式
独立模式。该模式中允许开发者独立管理多个包的版本更新。每次发布时,会得到针对每个包改动(patch, minor, major custom change)的提示。lerna 会配合 git,检查文件变动,只发布有改动的 package。
独立模式允许你更具体地更新每个包的版本,并且对于一组组件是有意义的。将这种模式和 semantic-release 结合起来就不会那么痛苦了。
独立模式允许开发者更新指定 package 的版本。将 lerna.json 中的 version 键设为 independent 来启用独立模式。
默认情况下,lerna 初始化 packages 值为["packages/"], 但是也可以使用自定义目录 ["modules/"]或["package1", "package2"].
lerna bootstrap
在当前 Lerna 仓库中执行引导流程(bootstrap)。安装所有 依赖项并链接任何交叉依赖。
lerna bootstrap --scope=@codemao/components
此命令至关重要,因为它让你可以 在 require() 中直接通过软件包的名称进行加载,就好像此软件包已经存在于 你的 node_modules 目录下一样。
lerna import
将本地路径 中的软件包导入(import) packages/ 中并提交 commit。
增加指定的包
删除指定的包
更新的工作
lerna publish
lerna diff [package?] 列出所有或某个软件包自上次发布以来的修改情况。
lerna run [script] 在每一个包含 [script] 脚本的软件包中运行此 npm 脚本。
lerna ls 列出当前 Lerna 仓库中的所有公共软件包(public packages)。
lerna clean
删除所有包下面的 node_modules 目录,也可以删除指定包下面的 node_modules。 注意: 不会删除 package.json 里面的依赖项定义,也不会删除 root 目录的 node_modules。
lerna clean --scope=@codemao/components
lerna 利用语义链接来实现这一目的。它还允许使用 yarn 工作空间,然后,将整个 Mono-Repo 方面的工作完全交给 yarn 工作空间的本地实现的功能。
在 Mono-Repo 工作流中使用 yarn 工作空间。 使用 lerna 的实用命令来优化多个包的管理,例如,选择性地执行 npm 脚本进行测试。
使用 lerna 发布软件包,因为 lerna 的版本和发布命令提供了复杂的功能。
通俗来讲就是:
包管理的能力交给 Lerna, 如版本管理; 依赖管理的能力交给 pnpm, 如依赖包的安装删除;
准备工作
npm install pm2 -g
npm install --global verdaccio
pm2 start verdaccio
verdaccio
pnpm install lerna -g
在当前根目录下执行: lerna init --independent
pnpm install lerna@5.3.0 -D -w
pnpm dlx lerna init
通过 lerna add @codemao/hooks packages/components
发包 lerna publish from-package
lerna publish 主要做了以下几件事:
Run the equivalent of lerna updated
to determine which packages need to be published.
If necessary, increment the version
key in lerna.json
.
Update the package.json
of all updated packages to their new versions.
Update all dependencies of the updated packages with the new versions, specified with a caret (^).
Create a new git commit and tag for the new version.
Publish updated packages to npm.
• 检查从上一个 git tag 之后是否有提交,没有提交就会显示 No changed packages to publish 的信息,然后退出 • 检查依赖了修改过的包的包,并更新依赖信息 • 提交相应版本的 git tag • 发布修改的包及依赖它们的包
如果 lerna.json 并没有更新,重试一下
如果已经更新,您可以强制重新发布。
采用 independent 模式
根据 Gi 提交信息,自动生成 changelog
eslint 规则检查
prettier 自动格式化代码
提交代码,代码检查 hook
遵循 semver 版本规范
JavaScript and TypeScript Monorepos
启动后