本文主要套路在npm
迁移到pnpm
时,patch包
不兼容的一些问题…
前言
在前端项目开发中,我们经常需要修改 node_modules
中第三方包的代码来修复 bug 或添加功能。npm 生态中的 patch-package
是一个广泛使用的解决方案,但当项目迁移到 pnpm 时,会遇到补丁包不兼容的问题。本文将详细介绍如何在 pnpm 环境下处理这种兼容性问题。
相关文章:
背景:为什么需要补丁包?
在我们的项目中,需要修改 @vant/cli
的 gen-package-entry.js
文件,添加自定义函数 initMobileUI
的导出逻辑。这个修改在每次 npm install
后都会丢失,因此需要使用补丁包来持久化这些修改。
一、pnpm patch 原生流程
1.1 创建补丁
pnpm 提供了原生的补丁功能,使用起来非常简单:
# 创建补丁的临时工作目录
$ pnpm patch <package-name@版本号> # 如:pnpm patch @vant/cli@5.1.0
执行后,pnpm 会输出临时目录路径:
You can now edit the following folder: /private/var/folders/.../vant-cli@5.0.1
Once you're done with your changes, run "pnpm patch-commit <path>"
1.2 修改代码
进入临时目录修改需要的文件:
cd /private/var/folders/.../vant-cli@5.0.1
# 编辑需要修改的文件
vim lib/compiler/gen-package-entry.js
在我们的案例中,需要添加以下修改:
// 在 genPackageEntry 函数中添加
const components = names.map(pascalize);
// 添加对 utils 的导入,特别是 initMobileUI
const utilsImport = `import { initMobileUI } from '${getPathByName('utils', pathResolver)}';`;
const content = `${genImports(names, pathResolver, namedExport)}
${utilsImport}
// ... 其他代码
// 在导出部分添加
${genExports(names, pathResolver, namedExport)}
export { initMobileUI };
注意:这里 进入临时目录,支持同时修改多个文件,提交所有修改后生成单一补丁包 。如:
- 修改
src/compile.js
:修复编译逻辑 - 修改
lib/config.js
:新增配置项
1.3 提交补丁
修改完成后,回到项目根目录提交补丁:
pnpm patch-commit /private/var/folders/.../vant-cli@5.0.1
会在 patches/
目录下生成 @vant__cli@5.1.0.patch
文件(注意是双下划线)。
自动生成的补丁文件:patches/vant-cli@5.0.1.patch,包含所有(单个或多个)文件变更。
1.4 补丁文件格式
pnpm 生成的补丁文件格式如下:
diff --git a/lib/compiler/gen-package-entry.js b/lib/compiler/gen-package-entry.js
index d07e0a7..b5b9200 100644
--- a/lib/compiler/gen-package-entry.js
+++ b/lib/compiler/gen-package-entry.js
@@ -47,7 +47,11 @@ export function genPackageEntry({ outputPath, pathResolver, }) {
const skipInstall = (((_b = vantConfig.build) === null || _b === void 0 ? void 0 : _b.skipInstall) || []).map(pascalize);
const version = process.env.PACKAGE_VERSION || getPackageJson().version;
const components = names.map(pascalize);
+
+ // 添加对 utils 的导入,特别是 initMobileUI
+ const utilsImport = `import { initMobileUI } from '${getPathByName('utils', pathResolver)}';`;
const content = `${genImports(names, pathResolver, namedExport)}
+${utilsImport}
const version = '${version}';
二、npm patch-package 兼容性问题
2.1 问题描述
当项目从 npm 迁移到 pnpm 时,原有的 patch-package
补丁文件(格式为 @vant+cli+5.1.0.patch
,使用加号)无法被 pnpm 识别和应用。
2.2 两种补丁格式的区别
特性 | npm (patch-package) | pnpm (原生) |
---|---|---|
文件命名 | @vant+cli+5.1.0.patch | @vant__cli@5.1.0.patch |
分隔符 | 加号 + | 双下划线 __ |
应用时机 | 手动或 postinstall | 自动应用 |
路径前缀 | node_modules/ | 相对路径 |
2.3 直接迁移的问题
如果直接使用 pnpm,原有的历史补丁文件将无法应用,导致:
- 历史功能修改丢失
- 需要重新创建所有补丁
- 可能影响项目的正常运行
三、兼容性解决方案
3.1 方案设计思路
为了同时支持历史补丁和新的 pnpm 补丁,我们采用了混合方案:
- pnpm 原生补丁:处理新的修改(如
initMobileUI
导出) - 自定义脚本:应用历史的 npm 补丁文件
- postinstall 钩子:确保补丁在依赖安装后自动应用
3.2 实现步骤
步骤1:创建补丁应用脚本
创建 apply-patches.js
文件:
const { execSync } = require('child_process');
const fs = require('fs');
console.log('应用自定义补丁...');
try {
// 应用历史的 npm 补丁文件
console.log('应用 vant-cli 历史补丁...');
try {
execSync('patch -p1 -i patches/@vant+cli+5.1.0.patch --forward --reject-file=-', {
stdio: 'inherit'
});
} catch (error) {
console.log('补丁可能已经应用过,继续执行...');
}
console.log('✅ 补丁应用完成');
} catch (error) {
console.error('❌ 补丁应用失败:', error.message);
process.exit(1);
}
步骤2:配置 package.json
在 package.json
中添加相关脚本:
{
"scripts": {
"patch": "node apply-patches.js",
"postinstall": "node apply-patches.js"
}
}
步骤3:补丁文件管理
项目中会同时存在两种补丁文件:
patches/
├── @vant+cli+5.1.0.patch # npm 格式,包含历史修改
└── @vant__cli@5.1.0.patch # pnpm 格式,包含新修改
3.3 实际案例:initMobileUI 导出问题
问题背景
需要在 @vant/cli
的构建产物中导出自定义的 initMobileUI
函数,但该函数在每次构建后都会丢失。
解决步骤
创建 pnpm 补丁:
pnpm patch @vant/cli@5.1.0 # 修改 gen-package-entry.js 添加 initMobileUI 导出逻辑 pnpm patch-commit '/path/to/temp/dir'
保留历史补丁:
- 保持原有的
@vant+cli+5.1.0.patch
文件 - 通过
apply-patches.js
脚本应用
- 保持原有的
验证结果:
# 检查构建产物 grep -n "initMobileUI" es/index.js lib/index.js # 输出: # es/index.js:110:import { initMobileUI } from "./utils"; # es/index.js:348: initMobileUI, # lib/index.js:22: initMobileUI: () => import_utils.initMobileUI,
常见问题处理
在迁移过程中可能遇到的依赖问题:
# TypeScript 类型声明问题
pnpm add -D @types/node postcss
3.4 完整工作流程
# 1. 安装依赖
pnpm install
# → pnpm 自动应用 @vant__cli@5.1.0.patch
# → postinstall 钩子执行 apply-patches.js
# → 脚本应用 @vant+cli+5.1.0.patch
# 2. 构建项目
npm run build
# → 所有补丁都已生效,构建成功
四、最佳实践和总结
4.1 核心方案优势
通过本文介绍的混合方案,我们成功解决了从 npm 到 pnpm 迁移过程中的补丁包兼容性问题。这个方案的核心优势在于:
- 向后兼容:保持历史补丁的有效性,避免重复工作
- 向前发展:充分利用 pnpm 的原生补丁功能
- 自动化管理:通过 postinstall 钩子和脚本实现自动应用
- 可维护性:清晰的文件结构和完善的错误处理机制
4.2 实施要点
补丁文件管理:
- 新功能使用 pnpm 原生补丁(
@package__name@version.patch
) - 历史修改保持 npm 格式补丁(
@package+name+version.patch
) - 通过自定义脚本统一管理和应用
- 新功能使用 pnpm 原生补丁(
关键注意事项:
- 避免冲突:两种补丁文件不要修改同一文件的同一部分
- 路径差异:npm 补丁使用
node_modules/
前缀,pnpm 补丁使用相对路径 - 版本控制:将所有补丁文件和应用脚本纳入版本控制
错误处理策略:
- 使用
--forward
参数避免重复应用补丁 - 补丁应用失败时不中断整个构建过程
- 提供清晰的错误信息和解决建议
- 使用
4.3 常见问题排查
# 问题1:补丁文件格式错误
# 解决:检查补丁文件的路径前缀和命名格式
# 问题2:补丁已经应用过
# 解决:使用 --forward 参数忽略已应用的补丁
# 问题3:权限问题
# 解决:确保对 node_modules 目录有写权限
4.4 推广应用
这种混合方案不仅适用于 @vant/cli
的修改,也可以推广到其他需要补丁包的场景中。在实际项目中,建议根据具体需求调整脚本逻辑,但核心思路保持不变。
希望这个方案能帮助到其他遇到类似问题的开发者,让项目迁移过程更加顺畅。
附录:完整代码示例
apply-patches.js 完整代码
const { execSync } = require('child_process');
const fs = require('fs');
console.log('应用自定义补丁...');
try {
// 1. 应用原有的补丁文件
console.log('应用 vant-cli 历史补丁...');
try {
execSync('patch -p1 -i patches/@vant+cli+5.1.0.patch --forward --reject-file=-', {
stdio: 'inherit'
});
} catch (error) {
console.log('补丁可能已经应用过,继续执行...');
}
console.log('✅ 补丁应用完成');
} catch (error) {
console.error('❌ 补丁应用失败:', error.message);
process.exit(1);
}
package.json 配置示例
{
"name": "@zto/mzui",
"version": "v3.12.0",
"scripts": {
"patch": "node apply-patches.js",
"postinstall": "node apply-patches.js",
"build": "vant-cli build && npm run build:rem",
"build:rem": "node ./build-rem.js"
},
"devDependencies": {
"@types/node": "^24.0.14",
"postcss": "^8.5.6",
"patch-package": "^6.4.7"
}
}
目录结构示例
project/
├── patches/
│ ├── @vant+cli+5.1.0.patch # npm 格式补丁
│ └── @vant__cli@5.1.0.patch # pnpm 格式补丁
├── apply-patches.js # 补丁应用脚本
├── package.json # 项目配置
└── src/
└── utils/
└── global-config.ts # initMobileUI 函数定义
最后, 希望大家早日实现:成为编程高手的伟大梦想!
欢迎交流~

本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!