版本发布与工程实践:Changesets、CI 与避坑
本节目标
- 用 Changesets 管理多包版本与发版
- 理解「原子变更」的发版流程
- 知道 Monorepo 最常见的几个坑与对策
多包各自维护版本号、各自发版,会乱套。Changesets 是社区主流方案——它把「要发版的变更」记成一个个 changeset 文件,发版时统一算版本、统一打 tag。
# 1) 初始化 changesets
pnpm add -Dw @changesets/cli
npx changeset init
# 2) 你改了 @my/ui 的 Button,登记一次变更
npx changeset
# 交互里选 @my/ui,选版本 bump 类型(patch/minor/major),写一句描述
# 3) CI 里「版本确定」+「发布」两步
npx changeset version # 根据所有 changeset 算出新版本号,更新各包 package.json,删掉 changeset 文件
npx changeset publish # 把版本变更的包发到 npm(自动跳过没变的)changeset version 的妙处是原子化:它一次性把所有相关包的版本号、依赖关系算对(比如 ui 升了 minor,依赖它的 web 也会跟着 patch),你不用手动对齐。通常把这两步放进 CI:有人合并变更就自动算版本,打 tag 后自动 publish。
# .github/workflows/release.yml 片段(概念示意)
on:
push:
branches: [main]
jobs:
release:
steps:
- run: pnpm install
- run: npx changeset version
- run: npx changeset publish
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}最后,盘点 Monorepo 最常见的坑:
| 坑 | 现象 | 对策 |
|---|---|---|
| 幽灵依赖 | 没声明却能 import 到别人的包 | 用 pnpm(严格隔离);CI 跑 pnpm dedupe --check |
| 构建慢 | 全量 tsc 好几分钟 |
tsc --build 增量 + Turbo 缓存 + include 收窄 |
| 循环依赖 | 导入到 undefined |
madge 检测 + 抽共享层 |
| 权限/secret 泄露 | CI 误发私有包 | changeset publish 配 private: true 包不发布;npm token 最小权限 |
# 增量编译:只重编「变更及下游」,大型 Monorepo 提速必备
pnpm --filter @my/web exec tsc --build名词解释
Changesets:多包版本管理工具。开发者用「changeset 文件」登记「哪个包、bump 什么版本、为什么」,发版时统一算版本号并 publish,避免手动对齐版本。
原子变更(Atomic Change):一次提交/发版整体生效或整体回滚,不会出现「A 升了 B 没跟上」的半成品状态。Changesets 的版本计算就是为保证原子性。
CI/CD:持续集成(自动跑测试/构建)与持续交付(自动发布)的流水线,是 Monorepo 自动发版的承载。
增量编译(tsc --build):基于 Project References,只重新编译「改动及其下游包」,大型 Monorepo 把几分钟的 tsc 压到秒级的关键。
课后练习
练习 1:changeset version 和 changeset publish 分别干什么?顺序能反过来吗?
答案:
version根据 changeset 算出新版本号、更新package.json、删除 changeset 文件;publish才真正把包发到 npm。必须先 version 再 publish——没算版本就发,发的还是旧版本号,且依赖关系对不齐。
练习 2:为什么用 pnpm 能减少「幽灵依赖」?
答案:pnpm 用「严格 node_modules 结构 + 软链接」,只有当包在自身
dependencies/devDependencies显式声明时才能 import 到;没声明却能用的是幽灵依赖,pnpm 默认直接报「找不到模块」,从机制上堵死。
总结
Monorepo 的终点不是「能跑」,而是「能稳稳地规模化协作与发版」。Changesets 用「登记变更 → 原子算版本 → 自动 publish」把多包发版从手工地狱变成流水线;tsc --build + Turbo 把构建压进秒级;而幽灵依赖、循环依赖、secret 泄露这些坑,靠 pnpm 隔离、madge 检测、最小权限 token 来兜底。我的整体判断:Monorepo 的复杂度不在「搭」,而在「治理」——工具链(pnpm + Turbo + Changesets)到位,它就是你团队协作的倍增器;反之则是另一座债务山。