发布网友 发布时间:2024-10-02 03:41
共1个回答
热心网友 时间:2024-12-09 19:16
前言本文是笔者在学习和应用lerna+yarnworkspace多包工程化管理模式的过程中,记录的一些使用和问题汇总,作为笔记和分享于大家阅读。
lerna管理方式属于Monorepo模式,这有别于传统的Multirepo单仓库应用模式,下面我们先来了解一下两者的区别。
模式对比Multirepo传统的项目开发模式,比如create-react-app、vue-cli等框架模板脚手架生成的项目。
优点:
各模块管理自由度高;
各模块仓库体积一般不会太大;
缺点:
仓库分散不好找,分支管理混乱;
版本更新频繁,公共模块版本发生变化,需要对所有模块进行依赖更新;
Monorepo优点:
统一的规范、构建工具;
方便版本管理和依赖,模块之间的引用调试都变得非常方便;
Multirepo的缺点就是它的优点。
缺点:
随着应用扩展,仓库体积将变大。
lerna+yarnworkspace应用场景作为业务组件库的工程环境搭建。实现单个业务组件作为单独的npm包进行管理和发布,无需将各个业务组件分开建立在多个Git仓库中,且它们的技术栈、构建工具、规范等都可以保持一致。
作为日常业务项目工程管理。比如有一个低代码业务需求,低代码核心工作区的交互都相同,不同的是业务场景(外层壳子和一些定制功能),低代码相关的模块都可以复用,我们只需在这个仓库内不断去扩展业务需求即可,达到核心代码的复用(当然,可能会想到将低代码核心作为线上包)。
LernaLerna是一个管理工具,用于管理包含多个软件包(package)的JavaScript项目,是Babel自己用来维护自己的Monorepo并开源出的一个项目。
它可以:
统一的一套规范、构建标准;
对相互耦合较大、相互独立的JS/Git库进行管理;
统一的工作流和CodeSharing(代码共享)。
下面我们从以下几个方面来熟悉Lerna:
Lerna管理模式;
Lerna入门指引;
Lerna管理命令;
Lerna配置文件;
Lerna应用Demo;
Lerna版本发布;
Lerna最佳实践;
Lerna注意事项。
Lerna管理模式lerna管理项目可以使用两种模式,默认固定模式,当使用lernainit-i命令初始化项目时,此时为独立模式。(模式是用来管理多个package发包时的方式)
固定模式:在发包时会检测packages下涉及到变更的包,给这些变更的包使用同一版本,未发生变更的包不应用改版本,且不做发布升级;发布时可通过lernapublishmajor(大)|minor(中)|patch(小)自定义版本。
独立模式(常用的模式):允许每个包有自己独立的版本号,在lernapublish发布时,需要为每个改动的库指定版本号(逐个询问需要升级的版本号)。此模式,lerna.json-version字段指定为independent。
Lerna入门指引全局安装Lerna:
npm?install?--global?lerna初始化git代码仓库:
git?init?lerna-repo?&&?cd?lerna-repo初始化Lerna:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files此时得到了这样一个仓库目录结构:
lerna-repo/??packages/??package.json??lerna.json其中packages中保存着每个独立的包模块。
安装lerna到仓库node_moles中:
npm?install至此,我们就完成了一个Lerna工程的初始化工作,下面我们掌握一些操作命令来管理Lerna。
Lerna管理命令lernainit将一个仓库初始化为lerna仓库(默认固定模式)。支持参数:
--independent/-i?–?使用独立的版本控制模式lernacreate「package」创建一个package到项目工程的packages下。
lernaadd「mole」
为每个package都安装指定依赖:
lerna?add?react为指定的package安装特定依赖:
lerna?add?react-dom?packages/package1?//?or?lerna?add?react-dom?--scope=package1添加依赖到根目录node_moles中:
npm?install?typescript?-Dpackage之间的相互依赖(会在package/package.json下添加该依赖):
lerna?add?package2?--scope?package1?//?or?lerna?add?package2?packages/package1lernapublish用于npm包版本发布,具体细节可看下文「Lerna版本发布」。
lernabootstrap用于将packages链接在一起(前提是相互依赖的库),并安装package下的依赖到package/node_moles。
注意,它不会安装根目录package.json的依赖,如果需要安装根目录依赖,请使用npminstall。
参数:
--hoist:依赖提升,把每个package下的依赖包都提升到工程根目录(删除包下的node_moles,将依赖安装在根目录,但依赖注册不会在package/package.json内删除,也不会在root/package.json内添加此依赖)
lernaclean删除各个包下的node_moles(不会删除根目录node_moles)。
lernals列出当前Lerna仓库中的所有公共软件包(publicpackages)。
lernarun「script」
运行每个包下面的script(如果某个包没有此script,将会报错)
git?init?lerna-repo?&&?cd?lerna-repo0运行某个包下面的script
git?init?lerna-repo?&&?cd?lerna-repo0?--scope?package1lernaexec「shell」允许去执行shell脚本
git?init?lerna-repo?&&?cd?lerna-repo2lernachanged检查自上次发布以来哪些软件包被修改过。
lernalink链接互相引用的库,当pakcage/package.json内明确了packages下的包时,才会将相关包链接到package/node_moles中。
lernainfo查看lerna及运行环境的信息。
Lerna配置文件在lerna.json配置文件内可以指定工作模式、packages的位置以及一些命令的默认参数定义,如下示例:
git?init?lerna-repo?&&?cd?lerna-repo3version:当前仓库的版本,Independentmode请设置为independent;
packages:指定包所在的目录,支持指定多个目录;
npmClient:允许指定命令使用的client,默认是npm,可以设置成yarn;
useWorkspaces:使用yarnworkspaces管理Monorepo;
command.bootstrap.npmClientArgs:指定默认传给lernabootstrap命令的参数;
command.publish.ignoreChanges:指定那些目录或者文件的变更不用触发package版本的变更;
command.publish.message:执行发布版本更新时的生成的commitmessage;
command.publish.registry:指定发布到的registryurl,比如可以发布到指定私服,默认是npmjs.org;
command.publish.conventionalCommits:lernaversion将生成CHANGELOG.mdfiles(如果设置了这个,lerna管理模式将直接使用固定模式,version=independent的配置将失效)。
Lerna应用Demo有了上面的基础使用的了解,下面我们通过一个简单Demo熟悉一下Lerna管理Packages的流程方式。
创建Lerna工程:
git?init?lerna-repo?&&?cd?lerna-repo4创建两个package:
git?init?lerna-repo?&&?cd?lerna-repo5在package中维护几行测试代码:
git?init?lerna-repo?&&?cd?lerna-repo6//lerna-mole2/lib/lerna-mole2.jsconstlernaMole1=require('lerna-mole1');mole.exports=lernaMole2;functionlernaMole2(){console.log('lerna-mole2');}lernaMole1();lernaMole2();
git?init?lerna-repo?&&?cd?lerna-repo7运行脚本:
git?init?lerna-repo?&&?cd?lerna-repo0?--scope?lerna-mole2哎呀,此时会看到终端报错信息:
Error:Cannotfindmole'lerna-mole1'
手动建立package之间的关联:
git?init?lerna-repo?&&?cd?lerna-repo9此时可以在lerna-mole2目录下看到生成了node_moles文件夹,并且在里面放置了和lerna-mole1一模一样的包(软链接)。
再来执行一次命令:
git?init?lerna-repo?&&?cd?lerna-repo0?--scope?lerna-mole2?终端输出:?lerna-mole1?lerna-mole2好啦,我们第一个简单Lerna应用编写完成。接下来就是发布工作。
Lerna版本发布packages下的包版本发布需要使用lernapublish,这个命令组合了这两个命令:lernaversion和npmpublish。
其中lernaversion针对Lerna的管理模式(固定模式和独立模式),在表现上有所不同。
但主要工作还是在进行npmpublish之前,去管理哪些包要进行发布,以及发布过程中生成的Gitcommit、Gittag的提交。
固定模式下的lernaversion:
找出从上一个版本发布以来有过变更的package;
根据当前lerna.json中的版本生成新的版本号;
更新涉及到变更package下的package.json版本号;
更新lerna.json文件中的版本号;
将version更新、生成的CHANGELOG.md文件带来的变动提交为一次commit;
基于这次commit为所有涉及到更新的package打上各自的tag;
推送commit和tags到远程仓库。
独立模式下的lernaversion:
找出从上一个版本发布以来有过变更的package;
提示开发者为需要更新的package选择(一组VersionSelect)要发布的版本号;
更新到package下的package.jsonversion版本号;
如果packages下其他包有依赖这个包,那么其他包的package.json此包版本也会更新;
将version更新、生成的CHANGELOG.md文件带来的变动提交为一次commit;
基于这次commit为所有涉及到更新的package打上各自的tag;
推送commit和tags到远程仓库。
这里需要注意一下lerna查找包变更的逻辑:
在当前分支,找到最近一次tag,将当前commit和tag进行比较,看哪些package下的文件发生了变更。
命令使用如下:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files1初次使用发布时可能会遇到以下一些问题和注意事项:
避免开发者自己去打tag。lerna发布时会自动生成tag,并且查找更新是基于tag来识别的,避免开发者手动打上tag后,影响lerna查找变更,可能会造成一些变更包没有按照预期发布。
避免多条分支同时进行。在多条分支同时进行时,可能会生成相同的版本号,从而发生版本冲突。解决办法:分支开发者之前应提前约定好版本。
lernapublish中途发布失败,如何进行重发布。有时候发布可能会失败(比如npm没有登录、没有使用npmjs镜像源),再次运行lernapublish时,因为tag已经打上了,无法再查找到更新,进行包的发布。
可以采用下面两种发布方式:
运行lernapublishfrom-git。会将当前标签中涉及的NPM包再发布一次。(不会再更新package.json,只是执行npmpublish);
运行lernapublishfrom-package。会将当前所有本地包中的package.json和远端npm比对,如果npm上不存在此包的最新版本,都执行一次npmpublish。
Lerna最佳实践目前业界使用最多的方案是:lerna+yarmworkspace结合的Monorepo方案,两者工作职责划分不同:
yarn处理依赖安装工作(只想做好包管理工具);
lerna处理发布流程。
此处内容可以在下文查看yarnworkspace使用指南。
Leran注意事项发布前,提交工作区的变更。在发布前,需要提交工作区的文件变更,否则终端会收到下面报错信息:
lernaERR!EUNCOMMITWorkingtreehasuncommittedchanges,pleasecommitorremovethefollowingchangesbeforecontinuing:
发布前,需使用npmjs.org镜像。在发布前,如果npm设置的镜像源为淘宝镜像,需要切换回npm镜像:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files2如果要发布一个Scope包:Scope是指具有“组织”的包,比如Babel的相关包都是这一格式:@babel/xxx,在发布一个具有Scope包时,需要确保Organization(组织)已在npm上创建,私有包需要收费,公共包则为免费。
在发布Scopepackage时,需要在package.json声明accesspublish:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files3发布意外中断,进行重发布:如果发布因为某些原因中断了,未发布成功,再次执行发布,会得到如下提示:
lernasuccessNochangedpackagestopublish
但由于包并未成功发布到npmjs上,这时可以执行以下命令进行重发布:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files4independent模式并未生效:在lerna.json下指定了version为independent,但是发布时却还是固定模式的流程,原因可能是lerna.json内配置了conventionalCommits:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files5可以将其配置移除得到解决。
固定模式如何自己指定版本:当我们执行lernapublish时,lerna会自定分配一个版本提供我们使用;但这个版本可能不是我们期望发布的版本;如何自己控制发布的版本呢,在发布时我们可以传递配置:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files6yarnworkspace对于Monorepo的工程,使用最多的方式是lerna结合yarnworkspace一起使用。
因为yarn在依赖管理上做的非常不错,适合我们业务场景的依赖模块管理。
而package的发布工作依旧交由lernapublish来运转。
下面我们从以下几个方面来熟悉yarnworkspace:
yarnworkspace管理工程;
yarnworkspace管理命令;
yarnworkspace入门实战。
yarnworkspace管理工程初始化工程的步骤和上面lerna的方式一样,与lerna不同的是,需要做以下配置:
在lerna.json中声明使用yarnworkspace进行依赖管理:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files7在root/package.json下必需包含workspaces数组,与lerna.json下的packages保持一致:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files8yarnworkspace管理命令yarn管理命令大致分为两类(容易混淆,这里先提及一下):
处理工程下指定的包模块时使用:yarnworkspace;
处理工程根目录全局或所有包模块时使用:yarnworkspaces。
yarninstall代替npminstall+lernabootstrap安装工程依赖。
它与lernabootstarp不同的是:
yarninstall会将package下的依赖统一安装到根目录之下。这有利于提升依赖的安装效率和不同package间的版本复用(有些包是需要私有依赖的,而私有依赖会被多个包安装多次,而提升依赖可以解决这一问题)。
yarninstall会自动帮助解决安装(包括根目录下的安装)和packageslink问题。
yarnadd「mole」
为每个package都安装指定依赖:
lerna?init//?lerna?info?Creating?package.json?//?lerna?info?Creating?lerna.json?//?lerna?info?Creating?packages?directory?//?lerna?success?Initialized?Lerna?files9为指定的package安装特定依赖:
lerna-repo/??packages/??package.json??lerna.json0注意,package1一定是packages/package1/package.jsonname字段,有时候pa