我前段时间参加了一个react为主的大前端我的项目,笼罩Web、Android、Ios三个平台。因为整个业务逻辑偏重在手机端,且Web端也是到了我的项目中期才开始启动,我在搭底层框架时就没有思考用相似react-native-web这样的框架把三端对立,而是别离以react-nativereact为主起了两个我的项目来应答。

因为无论哪一端,调用的后端微服务集群都是同一个,导致两个我的项目中还是不可避免的呈现了一些反复逻辑,我尝试了封装成npm包来重用逻辑,但仅限于哪些通用且变动较少工具类代码,对于变动频繁的业务逻辑代码,封装进去的npm包时不时就要更新版本,且抽出到我的项目之外也不易开发和调试,用起来分外麻烦,得失相当。

最近尝试了lerna,惊喜的发现它岂但能解决过后我的项目的痛点,还能额定带来一些多项目管理相干的益处。

引入lerna

lerna的名字来源于希腊神话中的九头蛇海德拉(Lernaean Hydra),长相可参考我的项目logo,拿它形容多我的项目工程是再贴切不过了。

lerna的引入比设想中简略,其实,与其说引入lerna,倒不如说是导入到lerna更适合,因为具体的做法是通过命令行创立了一个新的lerna我的项目,而后把所有我的项目导入进去。而且在导入的同时,每个我的项目的git提交记录也都合并在了一起。

lerna initlerna import 你本地的我的项目门路

每个被导入的我的项目都会被寄存在根门路的packages目录下,上面是我demo我的项目的截图。

应用lerna来治理我的项目依赖

引入lerna后,第一件事就是要解决装置依赖的问题,咱们须要用lerna add 命令来代替咱们习惯的npmyarn,比如说当初给截图中的rntest我的项目装置lodash,就要执行上面的命令,该命令的底层实现也还是调用哦npm install之类的命令。

lerna add lodash --scope=rntest

不过,执行后你会发现其余我的项目中package-lock.json都产生了变动,让人十分困惑,这背地的起因是跟增加依赖后主动执行的装置命令lerna bootstrap无关。

lerna的依赖晋升

lerna能够通过lerna bootstrap一行命令装置所有子项目的依赖包,而且在装置依赖时还有依赖晋升性能,所谓“依赖晋升”,就是把所有我的项目npm依赖文件都晋升到根目录下,这样能防止雷同依赖包在不同我的项目装置屡次。比方多个我的项目都用了redux,通过依赖晋升,只须要下载一次放到根目录的node_modules目录下,就可供其余所有我的项目来应用。不过,须要额定的参数--hoist让依赖晋升失效。

lerna bootstrap --hoist

然而主动执行lerna bootstrap命令是不带依赖晋升参数的,这就导致下面每个我的项目的lock文件都会被批改的起因。

当然,要解决这个问题也容易,能够通过lerna的配置来防止npm对lock文件的批改即可,写法如下:

yarn是lerna的最佳搭档

lerna默认应用npm作为装置依赖包工具,但也能够抉择其余工具。yarn在1.0版本之后提供了workspaces的性能,该性能从更底层的中央提供了依赖晋升,做的事件跟lerna一模一样。把它跟lerna放在一起看,几乎就像是为lerna量身定做一样。因而,举荐在lerna中搭配yarn一起应用。

把npm替换成yarn只需在lerna的配置文件增加两行代码即可,配置完当前立即顺畅百倍。

高效的代码重用

在我参加的这个大前端我的项目里,多端之间代码反复的局部蕴含redux中的业务逻辑、http申请的解决、代码标准工具的查看、git钩子中的自定义脚本等等。在lerna架构下,前两者可间接抽取到一个独立的我的项目,而后被其余我的项目援用,比方在我的demo中,能够像其余依赖包一样间接引入shared我的项目, lerna会自动识别并把它导向外部我的项目。

import shared from 'shared'

这跟间接封装成npm包的一大区别就是实时更新,批改立即可见,就像在同一个我的项目一样,不影响开发和调试。

git钩子和自定义脚本的重用

我尝试把解决git钩子的工具husky装置到了根目录,触发的事件和自定义脚本能笼罩到每个我的项目,给这部分代码重用带来了极大遍历。比方,不少我的项目会增加自定义脚本来束缚git commit提交时的音讯形容,在lerna架构下,只需写一次即可。

eslint的重用

那些经常须要在根目录增加配置文件的第三方依赖,比方eslintprettierbabel等,在lerna中无奈简略粗犷的晋升合并到一处。因而,对于eslint这种前端开发已不可或缺的工具,能够尝试将所有配置项抽取到独立我的项目,而后装置第三方依赖的形式引入,相似eslint-config-airbnbeslint-config-prettiereslint-config-google这样。

不得不说,即使不必lerna框架咱们也能够这么做,只不过在lerna框架下批改立即可见,不便了调试和开发。

lerna框架下的CI/CD

多我的项目的构造无疑给CI/CD带来挑战,好在支流的CI框架能完满解决这个问题。比方在gitlab上,only/changes参数齐全满足了咱们的需要,让咱们能够为每一个子项目设置独自的pipeline,比方当初咱们设置一个pipeline,只当rntest我的项目下的文件被批改时才会触发:

在lerna框架下,所有我的项目都合在一个工程里,但CI/CD并不用这样。通过把脚本中的要害参数配置到CI/CD的我的项目内里,共用同一份.gitlab-ci.yml文件,从而可能实现每个子项目对应一个独立的CI/CD我的项目,最终CI/CD构造如下图:

lerna框架下的子项目权限

因为所有的我的项目都归并到了一个lerna工程下,一旦有了拜访权限意味着你能够批改所有子项目中的代码,在理论的开发工作中多多少少会带来一些麻烦。比如说,开发web和开发mobile平台的是两个不同的团队,如果我作为web组的一员,一不小心批改了或删除了mobile我的项目的文件该怎么办?如果不退出任何限度,这种事件迟早会产生,我想这可能是lerna框架与生俱来的的痛点。

可怜的是,在lerna框架下,gitlab或github这类第三方代码托管平台,自身的权限治理性能无奈解决这问题。但好在有其余工具的帮忙能够缓解这种痛,我尝试用来束缚开源贡献者提交PR标准的工具dangerjs来实现权限分隔,利用的信息就是以后gitlab账号的用户名,看起来成果还不错。

这个工具会在合并MR的时候,判断出我gitlab账号没有权限批改rntest子项目内的文件,因而禁止合并此MR,并将这些信息主动增加到MR的评论里。当然,脚本判断的逻辑比拟简陋,仅用来做演示用,而对于dangerjs的局部我会另写一篇文章具体介绍。

结语

大前端我的项目将会是前端倒退的趋势,如何更好的治理大前端我的项目是每一位前端开发躲不开的课题。lerna框架通过合而为一的理念提供了一种解决方案,通过取长补短,咱们能够施展出lerna的最大效用。如果你还没有用过,兴许,下一个我的项目就能够试试看。

相干材料

lerna的github地址

文章中的demo