版本管理实战:Changeset 工作流完全指南(含中英文对照)

本文是《从零到一:构建现代化企业级 Monorepo 项目实战》系列的第五篇。前面我们搭建了项目和代码质量工具,这篇文章将讲解如何优雅地管理多包版本和发布流程。

🎯 本文目标

读完这篇文章,你将学会:

  • Changeset 的工作原理和优势
  • 完整的版本发布流程
  • 语义化版本控制实践
  • CHANGELOG 自动生成技巧
  • Changeset 中英文交互对照

📖 Monorepo 版本管理的挑战

传统方式的问题

bash 复制代码
# 场景:需要同时发布 3 个相关的包

# 😫 方式1:手动修改版本号
vim packages/shared/package.json    # 1.0.0 → 1.0.1
vim packages/utils/package.json     # 1.0.0 → 1.0.1
vim packages/ui/package.json        # 1.0.0 → 1.0.1

# 😫 方式2:使用 lerna
lerna version patch
# 所有包都升级,即使有些包没有改动

# 😫 方式3:使用 standard-version
pnpm exec standard-version
# 只能基于 commit 判断,不够灵活

理想的版本管理

bash 复制代码
# ✅ 期望的效果:
1. 灵活选择要更新的包
2. 自动处理依赖关系
3. 生成清晰的 CHANGELOG
4. 一键发布所有变更

🚀 Changeset 工作流

核心概念

Changeset = 变更集合

markdown 复制代码
一个 Changeset 包含:
1. 哪些包需要更新
2. 每个包的版本类型(major/minor/patch)
3. 变更描述(会出现在 CHANGELOG 中)

工作流程图

graph LR A[开发代码] --> B[添加 Changeset] B --> C[提交代码] C --> D[准备发布] D --> E[执行 version] E --> F[更新版本号] F --> G[生成 CHANGELOG] G --> H[构建项目] H --> I[发布到 NPM] I --> J[推送到远程]

📦 安装和配置

1. 安装 Changeset

bash 复制代码
pnpm add -Dw @changesets/cli
pnpm changeset init

2. 配置文件

json 复制代码
// .changeset/config.json
{
  "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "access": "public",
  "baseBranch": "master",
  "updateInternalDependencies": "patch",
  "ignore": ["@gdu-common/docs", "@gdu-common/build-config"]
}

配置说明:

字段 说明 推荐值
changelog CHANGELOG 生成器 @changesets/cli/changelog
commit 是否自动提交 false(手动控制更好)
access NPM 发布权限 publicrestricted
baseBranch 主分支名称 mastermain
ignore 不需要发布的包 文档、构建配置等

3. 添加脚本

json 复制代码
// package.json
{
  "scripts": {
    "changeset:add": "pnpm changeset add",
    "changeset:version": "pnpm changeset version",
    "changeset:publish": "pnpm build && pnpm changeset publish",
    "changeset:status": "pnpm changeset status",
    "uv": "pnpm changeset && pnpm changeset version"
  }
}

🎬 完整发布流程实战

场景:修复了一个 Bug

bash 复制代码
# 1. 修改代码
vim packages/ui/src/components/Button/button.vue

# 2. 添加 Changeset
pnpm changeset add

Changeset 交互流程(中英文对照)

步骤1:选择包

bash 复制代码
🦋 Which packages would you like to include?
   选择要更新的包

( ) @gdu-common/shared
( ) @gdu-common/utils
(●) @gdu-common/ui          # 空格选中
( ) @gdu-common/controls-sdk

→ 操作:空格键选择,回车确认

步骤2:选择 Major(主版本)

bash 复制代码
🦋 Which packages should have a major bump?
   选择需要主版本更新的包(不兼容的 API 变更)

( ) all packages
( ) @gdu-common/ui@1.0.0

→ 操作:如果不是破坏性变更,直接回车跳过

步骤3:选择 Minor(次版本)

bash 复制代码
🦋 Which packages should have a minor bump?
   选择需要次版本更新的包(新功能)

( ) all packages
( ) @gdu-common/ui@1.0.0

→ 操作:如果不是新功能,直接回车跳过

步骤4:Patch(补丁版本)

bash 复制代码
🦋 The following packages will be patch bumped:
   以下包将进行补丁更新(Bug 修复)

@gdu-common/ui@1.0.0

→ 说明:未选 major/minor 的包会自动归到 patch

步骤5:输入变更描述

bash 复制代码
🦋 Please enter a summary for this change
   请输入变更说明(会出现在 CHANGELOG 中)

Summary >> 修复 Button 组件点击事件bug

→ 操作:输入中文描述,回车确认

步骤6:确认

bash 复制代码
🦋 === Summary of changesets ===
🦋 patch:  @gdu-common/ui

🦋 Is this your desired changeset? (Y/n)
   这是你期望的变更吗?

→ 操作:输入 Y 或直接回车

生成的 Changeset 文件

markdown 复制代码
## <!-- .changeset/funny-elephants-dance.md -->

## "@gdu-common/ui": patch

修复 Button 组件点击事件bug

提交 Changeset

bash 复制代码
git add .changeset/funny-elephants-dance.md
git commit -m "docs: 添加变更记录"
git push

🔄 更新版本号

执行 version 命令

bash 复制代码
pnpm changeset version

自动执行的操作:

bash 复制代码
🦋 Running version command...

# 1. 读取所有 changeset 文件
📝 Found 1 changeset:
   - funny-elephants-dance.md

# 2. 更新 package.json
📦 @gdu-common/ui: 1.0.0 → 1.0.1

# 3. 更新依赖此包的其他包
📦 @gdu-common/docs (dev): @gdu-common/ui@1.0.0 → 1.0.1

# 4. 生成 CHANGELOG
📝 Generating CHANGELOG.md for @gdu-common/ui

# 5. 删除已处理的 changeset 文件
🗑️ Removing .changeset/funny-elephants-dance.md

✅ Version bump complete!

生成的 CHANGELOG

markdown 复制代码
<!-- packages/ui/CHANGELOG.md -->

# @gdu-common/ui

## 1.0.1

### Patch Changes

- 修复 Button 组件点击事件bug

提交版本更新

bash 复制代码
git add .
git commit -m "chore: 更新版本到 1.0.1"
git push

📦 发布到 NPM

构建并发布

bash 复制代码
# 1. 构建所有包
pnpm build

# 2. 发布到 NPM
pnpm changeset publish

自动执行:

bash 复制代码
🦋 Publishing packages to npm...

📦 @gdu-common/ui@1.0.1
✅ Published successfully!

🏷️ Creating git tag...
✅ Tag v1.0.1 created

🚀 Pushing tag to remote...
✅ Tag pushed

🎉 All packages published!

推送代码

bash 复制代码
git push --follow-tags

🎯 版本类型详解

Major - 主版本(破坏性变更)

示例:修改组件 API

typescript 复制代码
// ❌ 旧版本 1.0.0
<Button type="primary">点击</Button>

// ✅ 新版本 2.0.0
<Button variant="primary">点击</Button>  // type 改成 variant

// 版本:1.0.0 → 2.0.0

Minor - 次版本(新功能)

示例:添加新属性

typescript 复制代码
// 旧版本 1.0.0
<Button>点击</Button>

// 新版本 1.1.0
<Button loading>点击</Button>  // ✨ 新增 loading 属性

// 版本:1.0.0 → 1.1.0
// 向后兼容,不影响旧代码

Patch - 补丁版本(Bug 修复)

示例:修复 Bug

typescript 复制代码
// 旧版本 1.0.0 - 有 Bug
const handleClick = () => {
  emit('click') // 😱 忘记传递 event
}

// 新版本 1.0.1 - 修复
const handleClick = event => {
  emit('click', event) // ✅ 修复
}

// 版本:1.0.0 → 1.0.1

💡 高级用法

1. 预发布版本(Alpha/Beta)

bash 复制代码
# 进入预发布模式
pnpm changeset pre enter alpha

# 添加变更
pnpm changeset add

# 更新版本
pnpm changeset version
# 生成:1.0.0 → 1.0.1-alpha.0

# 继续添加变更
pnpm changeset add
pnpm changeset version
# 生成:1.0.1-alpha.0 → 1.0.1-alpha.1

# 退出预发布模式
pnpm changeset pre exit

# 最终发布
pnpm changeset version
# 生成:1.0.1-alpha.1 → 1.0.1

2. 批量更新多个包

bash 复制代码
pnpm changeset add

# 选择多个包
🦋 Which packages would you like to include?
(●) @gdu-common/shared    # 空格选中
(●) @gdu-common/utils     # 空格选中
(●) @gdu-common/ui        # 空格选中

3. 依赖自动更新

bash 复制代码
# ui 依赖 utils
{
  "dependencies": {
    "@gdu-common/utils": "workspace:^"
  }
}

# 当 utils 更新到 1.1.0 时
# ui 的 CHANGELOG 会自动记录:

## 1.0.5

### Patch Changes

- Updated dependencies
  - @gdu-common/utils@1.1.0

4. 查看即将发布的内容

bash 复制代码
# 查看状态
pnpm changeset status

# 输出
🦋 Changeset status

This branch has:
- 2 changesets that will bump packages

Packages to be bumped:
@gdu-common/ui: patch
@gdu-common/utils: minor

# 预览版本更新(不实际执行)
pnpm changeset version --dry-run

🔧 实际项目配置

package.json 配置

json 复制代码
{
  "name": "@gdu-common/ui",
  "version": "1.2.3",
  "publishConfig": {
    "registry": "http://jfrog.gdu-tech.com/artifactory/api/npm/gdu-npm-front/",
    "access": "public"
  },
  "repository": {
    "type": "git",
    "url": "https://gitlab.gdu-tech.com/front-group/template/gdu-common.git",
    "directory": "packages/ui"
  }
}

发布脚本优化

json 复制代码
// 根 package.json
{
  "scripts": {
    "uv": "pnpm changeset && pnpm changeset version",
    "publish:all": "pnpm build && pnpm changeset publish",
    "push": "git push --follow-tags"
  }
}

简化流程:

bash 复制代码
# 一键添加变更并更新版本
pnpm uv

# 一键构建并发布
pnpm publish:all

# 推送
pnpm push

📝 CHANGELOG 管理

自动生成的 CHANGELOG

markdown 复制代码
# @gdu-common/ui

## 1.2.0

### Minor Changes

- 添加 Button 组件的 loading 状态
- 支持自定义图标

### Patch Changes

- 修复 Input 组件的清除按钮样式问题
- Updated dependencies
  - @gdu-common/utils@1.1.0

## 1.1.0

### Minor Changes

- 新增 Input 组件
- 新增 Modal 组件

### Patch Changes

- 修复 Button 组件在 Safari 下的样式问题

CHANGELOG 最佳实践

markdown 复制代码
# ✅ 好的变更描述

- 修复 Button 组件在暗色模式下的对比度问题
- 添加 Input 组件的键盘导航支持
- 优化 Modal 组件的动画性能

# ❌ 不好的变更描述

- 修复 bug
- 更新组件
- 改进代码

🎯 实战演练:完整发布流程

场景:开发了一个新功能

bash 复制代码
# ===== 第1步:开发完成 =====
# 添加了 Button 组件的 loading 状态

# ===== 第2步:添加 Changeset =====
pnpm changeset add

🦋 Which packages would you like to include?
(●) @gdu-common/ui

🦋 Which packages should have a minor bump?
(●) @gdu-common/ui@1.1.0  # 新功能选 minor

🦋 Summary >> 添加 Button 组件的 loading 状态

🦋 Is this your desired changeset? (Y/n) >> Y

✅ Changeset added!

# ===== 第3步:提交 Changeset =====
git add .changeset/
git commit -m "docs: 添加变更记录"
git push

# ===== 第4步:准备发布 =====
# 所有功能开发完成,准备发布

# ===== 第5步:更新版本号 =====
pnpm changeset version

🦋 All files have been updated and committed

# 查看变更
git diff HEAD~1

# packages/ui/package.json
- "version": "1.1.0"
+ "version": "1.2.0"

# packages/ui/CHANGELOG.md 新增内容
+ ## 1.2.0
+ ### Minor Changes
+ - 添加 Button 组件的 loading 状态

# ===== 第6步:提交版本更新 =====
git add .
git commit -m "chore: 发布 v1.2.0"
git push

# ===== 第7步:构建 =====
pnpm build

Tasks:    4 successful, 4 total
Cached:   3 cached, 4 total
  Time:   2.1s

# ===== 第8步:发布到 NPM =====
pnpm changeset publish

🦋 Publishing packages to npm...
📦 @gdu-common/ui@1.2.0
✅ Published

# ===== 第9步:推送标签 =====
pnpm push

✅ 发布完成!

🎨 高级技巧

1. 一次更新多个包

bash 复制代码
# 场景:utils 添加了新函数,ui 使用了这个函数

pnpm changeset add

🦋 Which packages would you like to include?
(●) @gdu-common/utils     # utils 新增功能
(●) @gdu-common/ui        # ui 使用新功能

🦋 Which packages should have a minor bump?
(●) @gdu-common/utils@1.0.0  # utils minor

🦋 Which packages should have a patch bump?
(●) @gdu-common/ui@1.1.0     # ui patch(只是使用,不是新功能)

🦋 Summary >>
工具库新增 formatCurrency 函数
UI 组件使用新的货币格式化工具

2. 依赖自动升级

json 复制代码
// .changeset/config.json
{
  "updateInternalDependencies": "patch"
}

效果:

bash 复制代码
# utils 升级到 1.1.0
# ui 依赖 utils,自动升级一个 patch 版本

@gdu-common/utils: 1.0.0 → 1.1.0 (minor)
@gdu-common/ui: 1.0.0 → 1.0.1 (patch, 因为依赖更新)

3. 快捷命令

json 复制代码
{
  "scripts": {
    "uv": "pnpm changeset && pnpm changeset version"
  }
}
bash 复制代码
# 一个命令完成添加和更新
pnpm uv

# 交互完成后,自动执行 version
# 减少操作步骤!

📊 版本管理最佳实践

1. 语义化版本控制

严格遵循 SemVer:

复制代码
版本格式:主版本.次版本.补丁版本

1.2.3
│ │ │
│ │ └─ Patch:Bug 修复,向后兼容
│ └─── Minor:新功能,向后兼容
└───── Major:破坏性变更,不向后兼容

判断标准:

变更类型 版本类型 示例
修改 API 接口 Major 删除/重命名参数
添加新组件 Minor 新增 Button 组件
修复样式bug Patch 修复按钮颜色
优化性能 Patch 优化渲染性能
添加新属性 Minor Button 新增 loading
修改内部实现 Patch 重构内部逻辑

2. 变更描述规范

✅ 好的描述:

markdown 复制代码
- 添加 Button 组件的 loading 状态支持
- 修复 Input 组件在 IE11 下的兼容性问题
- 优化 Modal 组件的打开动画性能,减少 30% 的渲染时间

❌ 不好的描述:

markdown 复制代码
- 更新
- 修复bug
- 改进

3. 发布前检查

bash 复制代码
# 创建 pre-publish 脚本
{
  "scripts": {
    "prepublishOnly": "pnpm lint:all && pnpm build"
  }
}

# 发布前自动执行检查和构建

🤖 自动化发布

CI/CD 集成

yaml 复制代码
# .gitlab-ci.yml
version-check:
  stage: check
  script:
    - |
      if [ -d ".changeset" ] && [ "$(ls -A .changeset/*.md 2>/dev/null)" ]; then
        echo "✅ Found changeset files"
      else
        echo "⚠️ No changeset files found"
        echo "💡 Run: pnpm changeset add"
        exit 1
      fi

publish:
  stage: deploy
  only:
    - master
  script:
    - pnpm install
    - pnpm build
    - pnpm changeset publish

📈 实际效果

版本发布效率对比

方式 手动操作 Changeset 提升
修改版本号 5分钟 30秒 10x
生成 CHANGELOG 15分钟 自动
发布到 NPM 10分钟 2分钟 5x
总耗时 30分钟 3分钟 10x

版本管理准确性

bash 复制代码
# 使用 Changeset 前
- 忘记更新版本号:20% 的发布
- 版本号错误:10% 的发布
- CHANGELOG 缺失:50% 的发布

# 使用 Changeset 后
- 自动更新,100% 准确 ✅
- 自动生成 CHANGELOG ✅
- 依赖关系自动处理 ✅

🤔 常见问题

Q1: 多人协作时如何处理冲突?

场景: 两个人同时添加了 changeset

bash 复制代码
# 开发者 A
.changeset/cool-cats-sing.md
  → 更新 ui 组件

# 开发者 B
.changeset/brave-dogs-jump.md
  → 更新 utils 工具

# 合并后
pnpm changeset version
# ✅ 自动合并两个变更,生成统一的版本

Q2: 如何回滚发布?

bash 复制代码
# 1. 删除 NPM 上的版本(24小时内)
npm unpublish @gdu-common/ui@1.2.0

# 2. 删除 Git tag
git tag -d v1.2.0
git push origin :refs/tags/v1.2.0

# 3. 回滚代码
git reset --hard HEAD~1

Q3: 如何发布特定的包?

bash 复制代码
# 只发布 ui 包
pnpm changeset publish --filter @gdu-common/ui

# 或者在添加 changeset 时只选择 ui

Q4: Changeset 文件可以手动编辑吗?

markdown 复制代码
## <!-- .changeset/cool-update.md -->

"@gdu-common/ui": minor
"@gdu-common/utils": patch

---

添加新功能并修复相关bug

详细说明:

- UI 组件新增 loading 状态(minor)
- 工具函数修复边界情况(patch)

可以! 手动编辑可以更精确地控制版本和描述。

🎁 快速上手清单

初始化

bash 复制代码
# 1. 安装
pnpm add -Dw @changesets/cli

# 2. 初始化
pnpm changeset init

# 3. 配置 .changeset/config.json
# 4. 添加脚本到 package.json

日常使用

bash 复制代码
# 开发功能 → 添加变更
pnpm changeset add

# 准备发布 → 更新版本
pnpm changeset version

# 发布 → 构建并发布
pnpm build && pnpm changeset publish

# 推送
git push --follow-tags

检查清单

  • .changeset/config.json 配置正确
  • baseBranch 设置为你的主分支
  • ignore 包含不需要发布的包
  • 各包的 publishConfig 配置正确
  • Git 远程仓库配置正确

🎉 总结

Changeset 提供了:

核心价值

  1. 灵活的版本控制 - 精确控制每个包的版本
  2. 自动的依赖更新 - 依赖包自动同步版本
  3. 清晰的 CHANGELOG - 自动生成,格式统一
  4. 简单的工作流 - 3个命令完成发布

实际收益

  • 📈 版本管理效率提升 10 倍
  • 🎯 版本准确性 100%
  • 📝 CHANGELOG 覆盖率 100%
  • ⏱️ 发布时间从 30分钟 → 3分钟

关键命令

bash 复制代码
pnpm changeset add      # 添加变更
pnpm changeset version  # 更新版本
pnpm changeset publish  # 发布
pnpm changeset status   # 查看状态

在下一篇文章中,我将分享 VitePress 文档站点的搭建和优化,包括:

  • 自定义主题配置
  • 组件示例自动导入
  • 交互式文档
  • SEO 和性能优化

🔗 系列文章


Changeset 真的很好用!如果你也在用或者准备用,点个赞让我知道! 👍

你的版本管理用的什么方案?有什么坑要分享?评论区见! 💬

相关推荐
尘世中一位迷途小书童4 小时前
VitePress 文档站点:打造专业级组件文档(含交互式示例)
前端·架构·前端框架
甜瓜看代码4 小时前
666
前端
吃饺子不吃馅4 小时前
【八股汇总,背就完事】这一次再也不怕webpack面试了
前端·面试·webpack
Amos_Web4 小时前
Rust实战教程--文件管理命令行工具
前端·rust·全栈
li理4 小时前
鸿蒙相机开发入门篇(官方实践版)
前端
webxin6664 小时前
页面动画和延迟加载动画的实现
前端·javascript
逛逛GitHub5 小时前
这个牛逼的股票市场平台,在 GitHub 上开源了。
前端·github
m0_651593915 小时前
Netty网络架构与Reactor模式深度解析
网络·架构
细节控菜鸡5 小时前
【排查实录】Web 页面能打开,服务器能通接口,客户端却访问失败?原因全在这!
运维·服务器·前端