前言
所在开发团队内部因为项目比较多,所以在项目上大家采用了monorepo这种项目,也就是把团队内的所有项目都统一放到一个代码仓库里面,公共的一些包和组件抽取成独立子项目或者子组件放在同个monorepo项目里面,这样方便复用组件,相关的好处网络上也有不少的文章说到,这里就不展开细说。关于monorepo的模块管理工具其实有不少,在此之前有lerna yarn等,笔者所在的团队之前是用yarn来管理monorepo的,最近做一些优化,改用rushjs+pnpm来管理,这篇blog作为笔者的一些初探学习。
技术选型
rushjs 走读
Rush makes life easier for JavaScript developers who build and publish many NPM packages at once. If you’re looking to consolidate all your projects into a single repo, you came to the right place! Rush is a fast, professional solution for managing this scenario.
摘抄自官网的introduction,简单讲就是rush的出现方便js开发者一次构建和发布多个npm包,如果考虑所有的project都放到同一个代码仓库里面的话(monorepo)可以考虑使用rush,rush为此提供了快速有效的解决方案。
rush的一些特点(来自官方文档):
- A single NPM install: rush 将你所有的项目的依赖安装到一个公共的文件夹,这个的作用不仅仅只是类似于项目根目录下的package.json,rush还通过符号链(symlinks)去重构每个项目的node_modules.这个逻辑支持 pnpm npm yarn这几种包管理器。
- Automatic local linking: rush repo里面的每个项目都可以自动符号链到其他的项目,不需要发布包,也不需要任何npm link.
- Fast builds: rush会分析你的依赖图谱然后按照正确的顺序构建你的项目,如果两个项目之间是没有任何依赖的,rush会通过分开的进程来按照这些依赖,这种多进程的方式会比采用单线程的方式有明显的加速。
- Subset and incremental builds: 子集和增量构建,在一个非常大的代码仓库里面,如果只是想使用或者开发其中一两个项目,这时候我们可以指定构建单独的项目,rush rebuild –to 只对你的上游依赖关系进行干净的构建,当你做了一些更改之后
rush rebuild --from <project>
只对受影响的下游项目进行干净的构建。 - Cycli dependencies: 循环依赖关系,如果一个项目间接依赖于自己的旧版本时,循环中的项目使用最后发布的版本,而其他项目仍然获得最新的。(这个笔者看的时候不是特别理解,大概的意思就是可以很好的管理多个不同版本之间的循环依赖吧)
- Bulk publishing: 当需要对代码仓库里面的多个包做release的时候,rush会检测出那些包发生了更改,自动修改所有合适的版本号,然后在每个目录下执行
npm publish
- Changelog tracking: 有变动的时候自动将变动信息聚合到一个CHANGELOG.md文件
pnpm 相关
笔者也是接触不久,这方面也是参考掘金上其他同学的文章
juejin.cn/post/693204…
环境
- 环境,笔者使用的MAC,使用window的同学可能有些不一样,本文还是以MAC这种开发环境来描述
- 安装nodejs(出于开发方便可能还要装一下nvm,方便切换不同的node版本,这里提一句,nvm的安装还是有不少网络限制的,可能要走不少弯路。。。)
- 安装
yarn
非必须,个人的开发习惯是从npm
转换到yarn
的,所以自己本地是有安装yarn,有需要的可以自行安装
初始化工作
创建一个空的目录作为项目目录
mkdir eason-td-monorepo
cd eason-td-monorepo
复制代码
安装rush(使用yarn安装,或者使用npm也行)
yarn global add @microsoft/rush
复制代码
安装pnpm
yarn global add pnpm
复制代码
rush初始化项目
rush init
复制代码
使用编辑器打开,我这里用的是vscode,打开后看一下rush.json, emmm,一片警告
这时候不要慌,因为这是个json配置文件,json配置文件一般是不允许有comment的,vscode内置了一种类型叫JSON with Comments
,专门来识别这种类型,在右下角这里切换一下类型,切换到JSON with Comments
就不会显示警告报错了,这里不展开细讲
按上面的一步处理完之后,可以看到这边还是有一些拼写提示,虽然不是很重要,这里还是处理一下,可忽略该步骤
vscode打开setting,搜索cSpell.words
,添加进入这个pnpm
,之后应该就不会有报错提醒
rush.json里面可以指定运行的工具,这里是指pnpm
,npm
, yarn
这几个,我们这里指定pnpm就可以,其他的也可以玩一下,这里就不展开细讲
rush.json里面有一大堆配置和comment,emmm,这里也不仔细去研究,先看关键的,可以看一下projects
这个配置,因为我们要创建的是一个monorepo项目,所以到时候会有多个子项目在这个monorepo项目里面,所以到时候可以重点关注一下这个
再来看一下目录下的这个位置common/config/rush
,下面有一个对应的pnpmfile.js
文件,如果我们不是用pnpm的话,可以删除这个文件,当然啦,这里不要删除,我们用的就是pnpm
创建子项目
在项目的根目录项目创建一个apps
的路径,用于放置我们的子项目,我们这里创建一个landing的子项目
mkdir apps
cd apps
yarn create react-app landing
复制代码
运行完上面的命令后会回去install各种依赖,这个过程会有点长,然后我们回到上面说的那个rush.json
里面的projects
配置项,为我们新增的这个子项目增加一个项目名还有指定对应的目录,如下
运行完之后我们再运行一个命令,这时候只要是在项目目录下即可,可以不用在项目的根目录,rush自己会处理识别这个
rush update
复制代码
install完成之后,切换到apps/landing
目录下运行这个子项目,可以用npm、yarn或者rushx运行项目,rushx是在我们全局安装rush之后会带的一个命令,这里我们推荐用rushx
cd apps/landing
rushx start
复制代码
这时候会运行本地项目,然后在本地的浏览器打开该项目的页面,默认是3000端口,如果本地3000端口被占用的话会提示是否用其他端口,这时候输入yes即可
到这里一步的话我们的一个子应用算是跑起来了,我们这个项目是monorepo项目,所以单独一个项目是不够的,还需要多其他的子项目来实验,这里我们继续创建项目(control + c 结束运行刚刚的项目)
创建第二个子项目
回到项目根目录,创建一个libs目录,这个目录的作用类似于library,把一些共用的组件或者包之类的放在这个目录
cd ../../
mkdir libs
cd libs
复制代码
这个时候我们需要借助一个工具叫tsdx
来创建我们的子组件项目,创建一个components的子项目,tsdx会提供三个模板供选择,这里我们选react就可以
npx tsdx create components
复制代码
切换到创建出来的路径,运行一下子应用,运行没有报错,我们就control+c结束运行
cd components
rushx start
复制代码
新增多了一个project,这时候我们要在rush.json里面增加这个project的配置信息
这里我们给新创建的project命名为@shared/components
,需要注意的是,在我们创建的libs/components/package.json
里面有默认的name
,这里我们要保持两边统一,这样才可以识别到具体对应的project
回到项目根目录更新一下,这时候会重新安装依赖
cd ../../
rush update --purge
复制代码
运行结束之后我们尝试一下在landing这个项目里面安装刚刚声明的“包”,因为这时候rush知道我们引用的是一个本地monorepo项目里面的包,所以不会尝试着通过网络去npm查找这个对应的包
cd apps/landing
rush add --package @shared/components
复制代码
我们可以看到提示,现在是link的的project成功,下一步推荐运行build或者rebuild
rush build
复制代码
在landing项目中引用@shared/components,我们改一下apps/landing/src/App.js
import logo from './logo.svg';
import './App.css';
import { Thing } from '@shared/components';
function App() {
return (
<div className="App">
<Thing />
</div>
);
}
export default App;
复制代码
重新运行起这个apps/landing这个应用
rushx start
复制代码
可以看到这时候已经可以正常link引入本地的包了,到这一步基本上我们的monorepo项目就告一段落
结语
本次只是一个初探,关于rush和pnpm等工具还有很多不懂的地方,文中若有不妥之处,还望各位读者同学雅正。