Hi我擅长AI 大模型应用落地、意识解码与 AI 开发工具链。 创业路上用技术换时间一起把 AI 变成生产力 Changesets 3.0 实战构建纯 ESM 的 Monorepo 版本管理工作流在真实的软件开发中版本号不仅是一个标签它是对外发布的契约。在校学生或刚转行的同学在写个人项目时往往习惯用npm version patch一把梭但在包含多个相互依赖包的 Monorepo单体仓库中手动管理版本和变更日志会迅速演变成一场灾难。Changesets 是目前社区主流的文件驱动式版本管理工具它通过在代码库中生成简单的 Markdown 文件来记录变更进而在发布时自动计算版本号并生成 CHANGELOG。近期 Changesets 迎来了时隔七年的大版本 3.0 更新。它全面转向纯 ESM 发布安装体积大幅缩减 88% 降至 2.1MB并且对对等依赖的下游包默认升补丁版本。掌握这套工作流意味着你具备了企业级前端工程化的协作能力这是可以直接写进作品集的硬核技能。① 前置准备环境、账号、依赖在开始之前我们需要明确前置知识了解 Node.js 基本命令和package.json的常见字段。本教程的例子小而完整不依赖任何公司内部基础设施你可以在本地一键跑通。运行环境要求Node.js^22.11 || ^24 || 26Changesets 3.0 强制要求包管理器pnpm 9.x当前主流 Monorepo 首选工具初始化项目结构打开终端执行以下命令创建一个基础的 Monorepo 工作区# 创建项目根目录mkdirchangesets-v3-democdchangesets-v3-demo# 初始化 package.jsonnpminit-y# 安装 pnpm如果尚未全局安装npminstall-gpnpm# 创建 pnpm-workspace.yaml 定义工作区echopackages:\n- packages/*pnpm-workspace.yaml# 安装 Changesets v3 为开发依赖pnpmadd-Dchangesets/cli^3.0.0# 初始化 Changesets 配置pnpmchangeset init执行完毕后你的项目根目录应包含以下文件结构package.jsonpnpm-workspace.yaml.changeset/目录包含config.json和README.md② 步骤 1配置纯 ESM 与 Changesets 参数目标将根项目配置为纯 ESM 模式并调整 Changesets 配置以适配 3.0 的新特性。操作修改根目录的package.json添加type: module并配置 Changesets 脚本。同时修改.changeset/config.json。根目录package.json关键部分{name:changesets-v3-demo,version:1.0.0,type:module,private:true,scripts:{changeset:changeset,version:changeset version,publish:changeset publish},devDependencies:{changesets/cli:^3.0.0}}修改.changeset/config.json中的updateInternalDependencies确保其设置为patch这也是 3.0 的默认行为优化点{$schema:https://unpkg.com/changesets/config3.0.0/schema.json,changelog:changesets/cli/changelog,commit:false,fixed:[],linked:[],access:public,baseBranch:main,updateInternalDependencies:patch,ignore:[]}预期输出配置文件保存无报错。失败时怎么查如果运行脚本报ERR_UNKNOWN_FILE_EXTENSION说明你的 Node 版本低于 22.11或项目未正确设置type: module。请使用node -v检查版本。③ 步骤 2创建相互依赖的工作区包目标创建两个包demo/utils和demo/ui其中demo/ui将demo/utils声明为对等依赖模拟真实场景下的组件库分离。操作# 创建包目录mkdir-ppackages/utils packages/ui# 初始化 demo/utilscdpackages/utilsnpminit-y--scopedemo# 手动编辑 package.json 如下packages/utils/package.json{name:demo/utils,version:1.0.0,type:module,main:./index.js,exports:./index.js}创建packages/utils/index.jsexportfunctionformatString(str){returnstr.trim().toLowerCase();}回到根目录初始化demo/uicd../../cdpackages/uinpminit-y--scopedemopackages/ui/package.json注意这里的peerDependencies{name:demo/ui,version:1.0.0,type:module,main:./index.js,exports:./index.js,peerDependencies:{demo/utils:1.0.0}}创建packages/ui/index.jsimport{formatString}fromdemo/utils;exportfunctionrenderButton(label){constsafeLabelformatString(label);returnbutton${safeLabel}/button;}在根目录执行pnpm install建立工作区软链接。预期输出终端提示Progress: resolved X, reused X并显示demo/ui和demo/utils被成功链接。失败时怎么查如果提示找不到demo/utils请检查pnpm-workspace.yaml是否在根目录且通配符路径是否正确。④ 步骤 3添加 Changeset 并消费变更目标模拟修复了demo/utils的一个 Bug通过 Changesets 记录此次变更并观察 3.0 如何自动处理对等依赖的下游包。操作在根目录执行以下命令启动交互式生成流程pnpmchangeset终端会出现交互提示选择要变更的包使用空格键选中demo/utils回车确认。选择 SemVer 类型选择patch代表补丁版本修复。输入变更摘要输入fix: handle empty string in formatString回车两次确认。此时项目根目录的.changeset/下会生成一个类似spicy-actors-smile.md的文件。接下来执行版本消费命令pnpmversion预期输出packages/utils/package.json的版本号从1.0.0升级为1.0.1。关键变化3.0 核心特性packages/ui/package.json的版本号也会自动从1.0.0升级为1.0.1并且其peerDependencies中的demo/utils会同步更新为^1.0.1。两个包目录下各自生成了CHANGELOG.md文件记录了刚才输入的摘要。失败时怎么查如果demo/ui的版本没有被升级检查.changeset/config.json中的updateInternalDependencies是否被误设为minor或被关闭。⑤ 完整示例将上述步骤串起来这是一份可以直接照抄的最终目录结构与核心配置摘要changesets-v3-demo/ ├── .changeset/ │ ├── config.json │ └── README.md ├── packages/ │ ├── ui/ │ │ ├── CHANGELOG.md │ │ ├── index.js │ │ └── package.json # peerDeps 自动升补丁 │ └── utils/ │ ├── CHANGELOG.md │ ├── index.js │ └── package.json # 版本升至 1.0.1 ├── package.json └── pnpm-workspace.yaml面试/作业里常被追问的点面试官常问“如果 A 包依赖 B 包B 发了 major 版本A 会自动发 major 吗”答案是不会。Changesets 默认且 3.0 中进一步固化的行为是对下游依赖进行patch级别的升级以避免未经验证的破坏性变更蔓延。如果需要同步 major需要开发者手动添加针对 A 的 changeset。⑥ 常见问题FAQQ1: 运行pnpm changeset时报错Error [ERR_REQUIRE_ESM]: require() of ES Module是什么原因解决方案这是因为 Changesets 3.0 已经是纯 ESM 包。你的 Node.js 版本必须满足^22.11或更高。请使用nvm use 22.11或升级 Node 环境后再试。Q2: 为什么我执行changeset version后CHANGELOG.md 里的中文变成了乱码解决方案Changesets 默认使用 UTF-8 编码读写。请确保你的终端编码为 UTF-8并且使用的文本编辑器如 VS Code在右下角状态栏显示的文件编码也是 UTF-8而非 GBK 或其他系统默认编码。Q3: 我只想发布某个特定的包不想发布整个工作区该怎么做解决方案在.changeset/config.json中你可以利用ignore数组。例如ignore: [demo/ui]这样在执行version和publish时Changesets 会跳过该包即使它的依赖发生了变化也不会触发版本升级。Q4: 如何在 CI/CD如 GitHub Actions中自动发布解决方案官方提供了changesets/action。在你的 CI 配置中监听push事件到主分支运行pnpm changeset version如果检测到package.json有变更则提交回主分支并运行pnpm changeset publish。需要配置NPM_TOKEN环境变量以获取发布权限。最佳实践强制使用 ESM 导出在所有子包的package.json中显式声明type: module和exports字段避免使用 CommonJS 的module.exports以彻底拥抱 3.0 的纯 ESM 架构减少打包工具的兼容性开销。对等依赖的精细化控制对于强耦合的组件库如 UI 组件与其主题包在config.json中使用linked选项让它们的版本号保持一致对于松散的插件系统保持默认的patch更新策略防止插件因核心库的小更新而被迫频繁发版。将 Changeset 文件纳入代码审查在 PR 中要求必须包含.changeset/*.md文件。没有变更记录的 PR 一律打回这是保证 CHANGELOG 连贯性和发布纪律的关键。分离 Version 与 Publish 权限在本地或普通 CI 节点只运行changeset version计算版本和生成日志将代码提交回仓库后在受保护的发布节点单独运行changeset publish避免本地误操作将未测试的包推送到 npm 仓库。锁定包管理器版本在package.json中添加packageManager: pnpm9.x.x字段配合only-allow依赖强制团队所有成员使用统一的包管理器防止因锁文件格式不同导致的依赖解析差异。