一、Lerna包缓存的核心价值
很多做大型前端项目的朋友,应该都遇到过这样的糟心事:一个项目里塞了十几个甚至几十个子包,每次改完代码要构建,明明只有一个子包改了一行,整个项目却要花十几分钟甚至半小时重新编译所有子包;更烦的是发布的时候,没改的包还要再打包一遍,慢不说还容易出问题。
Lerna就是专门用来管这种多包项目(专业点叫Monorepo)的工具,而包缓存就是Lerna里能解决上面糟心事的核心功能。简单说,它的作用就是把之前构建过的包的结果存起来,下次再构建的时候,如果这个包的代码没改,就直接拿之前存的结果用,不用再重新编译。
举个例子,你有个叫utils的工具包,里面是各种通用函数,第一次构建的时候Lerna会把它的编译结果存到缓存里。之后你改了另一个叫app的业务包,构建的时候Lerna会检查utils的代码、依赖有没有变化,发现没变就直接用缓存里的结果,只编译app,速度一下子就能提好几倍。
二、Lerna包缓存的基础配置
要用上Lerna的缓存,首先得确保你用的是Lerna 4及以上的版本,老版本的缓存功能不完善。接下来我们一步步说怎么配置,所有示例统一用Node.js + Lerna的技术栈,先明确技术栈:Node.js 16+、Lerna 6.4.1、npm 8+。
2.1 开启基础缓存
Lerna 4以后,缓存功能默认是开启的,但默认配置可能不够用,我们先从最基础的配置改起。首先在项目根目录的lerna.json文件里加缓存相关的配置:
{
"version": "independent", // 子包版本独立管理,也可以用fixed模式
"npmClient": "npm", // 用npm做包管理工具,也可以换成yarn
"command": {
"build": {
"cache": true, // 开启构建缓存
"cacheLocation": "./node_modules/.cache/lerna", // 缓存文件存的位置
"ignore": ["src/**/*.test.ts"] // 忽略这些文件的变化,改了也不触发重新构建
}
}
}
这里的ignore配置很实用,比如你改了测试文件,测试文件的变化不会影响包的编译结果,所以可以把测试文件排除,避免没必要的重新构建。
2.2 配置缓存的内容校验规则
默认情况下,Lerna判断一个包要不要重新构建,会检查包的package.json、src目录下的所有文件、还有依赖的版本有没有变化。但有时候我们需要自定义这个规则,比如某个子包的编译结果只和src下的index.ts有关,其他文件改了不影响,就可以这么配置:
{
"version": "independent",
"npmClient": "npm",
"command": {
"build": {
"cache": true,
"cacheLocation": "./node_modules/.cache/lerna",
"ignore": ["src/**/*.test.ts"],
"contentHash": ["src/index.ts"] // 只根据index.ts的内容判断缓存是否有效
}
}
}
这个配置的意思是,只有src/index.ts的内容变了,才会重新构建这个包,其他文件改了都不会触发。如果你的子包结构固定,这个配置能大大减少缓存失效的概率。
三、缓存优化的进阶技巧
基础配置能解决大部分问题,但如果要进一步提升效率,还有几个进阶技巧可以用。
3.1 配置缓存的过期时间
有时候缓存里的文件会越来越多,占硬盘空间,或者某个包的缓存虽然有效,但放了太久,你想定期清理。可以给缓存加过期时间,超过时间的缓存会自动失效:
{
"version": "independent",
"npmClient": "npm",
"command": {
"build": {
"cache": true,
"cacheLocation": "./node_modules/.cache/lerna",
"ignore": ["src/**/*.test.ts"],
"cacheMaxAge": 86400000 // 缓存过期时间,单位是毫秒,这里是1天
}
}
}
这个配置适合那些经常会有大版本更新的项目,比如你每周会发一次大版本,那么缓存保留1天就够了,不用留太久占空间。
3.2 配置缓存的共享规则
如果你的团队是多人协作,大家各自构建的时候都会生成自己的缓存,能不能把缓存共享起来?比如A同事构建过的包,B同事直接用,这样大家都省时间。
Lerna支持把缓存存到共享服务器上,比如用S3或者自己搭的存储服务。我们先看本地共享的配置,适合团队用同一个局域网服务器的情况:
{
"version": "independent",
"npmClient": "npm",
"command": {
"build": {
"cache": true,
"cacheLocation": "./node_modules/.cache/lerna",
"ignore": ["src/**/*.test.ts"],
"cacheProvider": "s3", // 用S3作为缓存提供者
"cacheProviderOptions": {
"bucket": "lerna-cache-bucket", // 存储缓存的桶名
"region": "us-east-1", // 存储的区域
"accessKeyId": "你的访问密钥ID",
"secretAccessKey": "你的访问密钥"
}
}
}
}
配置好之后,只要有一个人构建过某个包,其他人构建的时候就会从S3拉取缓存,不用自己再编译。这个配置对团队协作的项目特别有用,尤其是大家都在改同一个大项目的时候,能省很多重复的时间。
3.3 配置发布时的缓存复用
发布的时候,Lerna默认会重新构建所有要发布的包,哪怕这些包之前已经构建过。我们可以配置发布的时候复用缓存,避免重复构建:
{
"version": "independent",
"npmClient": "npm",
"command": {
"build": {
"cache": true,
"cacheLocation": "./node_modules/.cache/lerna",
"ignore": ["src/**/*.test.ts"]
},
"publish": {
"useNpm": true,
"buildCache": true // 发布时复用构建缓存
}
}
}
这个配置的意思是,发布的时候会检查每个要发布的包有没有有效的缓存,如果有就直接用缓存里的结果打包发布,不用再重新构建。比如你之前已经构建过utils包,现在要发布它,就直接拿之前的结果用,速度快很多。
四、缓存的应用场景、优缺点和注意事项
4.1 应用场景
Lerna的包缓存适合所有用Lerna管理的Monorepo项目,尤其是以下几种场景:
- 子包数量多的项目:比如有20个以上子包的项目,每次构建都重新编译所有包,缓存能把构建时间从几十分钟降到几分钟。
- 频繁修改的项目:比如业务迭代快,每周都要改好几个子包,每次改都重新构建,缓存能节省大量时间。
- 团队协作的项目:多人同时开发,共享缓存能让大家不用重复构建,提升整个团队的效率。
- 发布频繁的项目:比如每周都要发布多个子包,发布时复用缓存能加快发布流程,减少发布时间。
4.2 技术优缺点
优点
- 提升构建速度:最核心的优点,能大幅减少重复构建的时间,尤其是子包多的项目,效果非常明显。
- 节省资源:不用重复编译,能减少CPU、内存的消耗,也能节省硬盘空间(如果配置了缓存过期)。
- 提升发布效率:发布时复用缓存,能加快发布流程,减少发布过程中可能出现的问题。
- 配置简单:大部分配置只要改lerna.json就行,不用写复杂的脚本,容易上手。
缺点
- 缓存失效的风险:如果配置不当,比如contentHash配置错了,会导致本该重新构建的包没重新构建,或者不该重新构建的包频繁重新构建。
- 共享缓存的安全问题:如果用S3等共享缓存,密钥管理不当可能会有安全风险,比如密钥泄露导致缓存被篡改。
- 缓存占空间:如果不配置过期时间,缓存会越来越大,占硬盘空间,尤其是子包多的项目,缓存可能会占几个G的空间。
- 依赖变化的影响:如果某个子包的依赖版本变了,缓存会失效,需要重新构建,这是正常的,但如果依赖版本经常变,缓存的效果会打折扣。
4.3 注意事项
- 版本要够:一定要用Lerna 4及以上的版本,老版本的缓存功能有很多bug,效果不好。
- 配置要合理:ignore和contentHash的配置要符合项目的实际情况,比如如果你的子包的编译结果依赖package.json里的某个字段,就不要把package.json排除在contentHash之外。
- 定期清理缓存:如果不配置过期时间,要定期手动清理缓存,比如执行
npx lerna clean命令,能清理所有缓存和node_modules。 - 共享缓存的安全:如果用S3等共享缓存,密钥要妥善保管,不要硬编码在lerna.json里,最好用环境变量来管理,比如:
{
"version": "independent",
"npmClient": "npm",
"command": {
"build": {
"cache": true,
"cacheLocation": "./node_modules/.cache/lerna",
"cacheProvider": "s3",
"cacheProviderOptions": {
"bucket": "lerna-cache-bucket",
"region": "us-east-1",
"accessKeyId": "${AWS_ACCESS_KEY_ID}",
"secretAccessKey": "${AWS_SECRET_ACCESS_KEY}"
}
}
}
}
然后在环境变量里配置AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY,这样就不会把密钥暴露在配置文件里了。 5. 测试缓存效果:配置完缓存后,要测试一下效果,比如改一个子包的代码,看构建时间有没有减少,改一个被ignore的文件,看缓存有没有失效,确保配置正确。
五、常见问题排查
有时候配置了缓存,但发现缓存没生效,或者效果不好,可能是以下几个原因:
- Lerna版本太低:检查Lerna的版本,用
npx lerna -v命令查看,如果版本低于4,升级到最新版本。 - 配置写错了:检查lerna.json里的配置,比如cache有没有设为true,cacheLocation的路径对不对,有没有语法错误。
- 缓存被清理了:比如执行了
npx lerna clean命令,或者node_modules被删了,缓存就没了,需要重新构建生成缓存。 - 依赖版本变化:如果某个子包的依赖版本变了,缓存会失效,需要重新构建,这是正常的。
- 共享缓存配置错误:如果用了S3等共享缓存,检查bucket、region、密钥有没有配置对,网络能不能连接到S3。
如果遇到问题,可以先执行npx lerna build --verbose命令,看详细的构建日志,日志里会说明为什么某个包的缓存失效了,能帮你快速定位问题。
六、文章总结
Lerna的包缓存是提升Monorepo构建和发布效率的重要工具,合理配置缓存能大幅减少重复构建的时间,提升开发和发布的效率。基础配置能解决大部分问题,进阶配置比如共享缓存、发布时复用缓存能进一步提升效率,尤其是团队协作的项目。
配置缓存的时候要注意版本、配置合理性、安全和定期清理缓存,遇到问题可以通过日志排查。只要配置得当,Lerna的包缓存能给大型Monorepo项目带来非常明显的效率提升,让开发者不用再花大量时间等待构建和发布,把精力放在业务开发上。
评论
围绕“Lerna包缓存优化:提升Monorepo构建与发布效率的配置技巧”参与讨论