大前端项目代码重用,也许lerna是最好的选择

我前段时间参与了一个react为主的大前端项目,覆盖Web、Android、Ios三个平台。因为整个业务逻辑侧重在手机端,且Web端也是到了项目中期才开始启动,我分别以react-nativereact分开建了两个项目。javascript

但是,后端微服务集群是同一个,两个项目调用的API大多同样,致使两个项目中不可避免的出现了一些重复逻辑。好比说写在redux中的业务逻辑代码,写在http拦截器中的请求处理代码等等,这些重复的部分从目录名和文件名便可肉眼看出。前端

我尝试了将重复部分封装成独立的npm包供两个项目引用,但这个作法仅适用于变化频率较小的工具类代码,一旦封装了变化频繁的业务逻辑代码,用起来也麻烦不断。首先,抽出到项目以外但不易开发和调试,其次,在项目开发过程当中业务代码更新太频繁,经常要不断升级版本。权衡利弊,最终得不偿失。java

那么,有没有办法更好的重用这部分代码逻辑呢?有没有办法把这两个项目的通用代码抽取成独立项目,可是又能避开封装成独立npm包的弊端呢?react

我最近尝试引入lerna框架,把这个大前端项目架构做为参照物来改造,惊喜的发现它不但能解决当时项目的痛点,还能额外带来一些多项目管理相关的好处。git

事实上,开源社区早已有不少项目使用了这种多项目合而为一的方案,且采用了lerna框架的代码库也大多耳熟能详,好比国外的有babelcreate-react-appreact-routerjest、以及国内跨端小程序框架Tarogithub

最后通过改造,两个项目合并成一个,重复的代码逻辑也被抽取成另外一个独立项目,整个项目结构变成了下面图示这样。web

引入lerna

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

lerna的引入比想象中简单,其实,与其说引入lerna,倒不如说是导入到lerna更合适,由于具体的作法是经过命令行建立了一个新的lerna项目,而后把全部项目导入进去。并且在导入的同时,每一个项目的git提交记录也都合并在了一块儿。json

lerna init
lerna import 你本地的项目路径
复制代码

每一个被导入的项目都会被存放在根路径的packages目录下,下面是我demo项目的截图,一共引入了三个子项目:rntest, web-app, shared。分别表明mobile,web和可重用的逻辑代码。redux

使用lerna来管理项目依赖

引入lerna后,第一件事就是要处理安装依赖的问题,咱们须要用lerna add 命令来代替咱们习惯的npmyarn,好比说给rntest项目安装lodash,就要执行下面的命令。

lerna add lodash --scope=rntest
复制代码

不过,执行后你会发现其余项目中package-lock.json都发生了变化,让人很是困惑,这背后的缘由是跟添加依赖后自动执行的安装命令lerna bootstrap有关。

lerna的依赖提高

lerna能够经过lerna bootstrap一行命令安装全部子项目的依赖包,并且在安装依赖时还有依赖提高功能,所谓“依赖提高”,就是把全部项目npm依赖文件都提高到根目录下,这样能避免相同依赖包在不一样项目安装屡次。好比多个项目都用了redux,经过依赖提高,多个项目一共只须要下载一次便可。不过,须要额外的参数--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的最大效用。假如你尚未用过,也许,下一个项目就能够试试看。

相关资料

yarn的workspaces

lerna的github地址

文章中的demo

相关文章
相关标签/搜索