日常团队协作过程中,由于现状不得不使用相对滞后版本的依赖包,但又期望使用新版本的某些功能…
一、背景
通常情况下,遇到需要支持的底层功能,开发人员直接升级依赖包版本即可,但总有意外。比如:
- 日常团队协作开发不同于个人独立开发,因为历史原因,不得不使用某个依赖包相对滞后的某个版本,但又需要新版本依赖包的某些功能。
- 新版本包存在未知的bug,但你的团队期望使用新版本中的某些已验证功能。
- 依赖包已经更新到最新版本,但存在官方未修复的bug。
构建补丁(patch) 通常用于修复特定包中的问题,而不需要等待上游包发布新的版本。
二、patch-package打补丁
以下是一个典型的步骤:
1.生成补丁
I. 安装目标package & patch-package
# 安装目标package
$ pnpm add <package-name> # pnpm 安装需修补的包
$ pnpm list <package-name> # pnpm 查看包的安装位置
$ npm install <package-name> # npm 安装需修补的包
# 安装 patch-package 工具,以便管理和应用补丁
$ npm i patch-package --save-dev
II. 修改npm包
以本次需求为例,需要打补丁的包是@vant/cli 5.1.0
,无论是新老版本都没有支持我需要的功能。所以,
- 打开目标项目工程(A)/node_modules文件夹,确认版本是5.1.0
- 稳妥起见,打开一个废弃的前端工程(B),安装
@vant/cli 5.1.0
(我这里偷懒使用的项目工程),打开node_modules/@vant/cli
目录- 场景1: 将你期望的新版本对应功能,复制粘贴到当前工程(B)对应
node_modules/xxx
目录文件内 - 场景2: 自行修复bug或diy你期望的功能,到当前工程(B)
node_modules/xxx
对应目录文件内
- 场景1: 将你期望的新版本对应功能,复制粘贴到当前工程(B)对应
npm run dev
启动工程(B),测试功能是否满足期望。
III. 生成补丁文件
功能符合预期,此时cd到工程(B)根木录下,执行如下命令生成补丁文件:
$ npx patch-package <package-name> # 示例: npx patch-package @vant/cli
终端可看到执行记录如下:
$ npx patch-package @vant/cli
patch-package 6.4.7
• Creating temporary folder
• Installing @vant/cli@5.1.0 with npm
• Diffing your files with clean files
✔ Created file patches/@vant+cli+5.1.0.patch
💡 @vant/cli is on GitHub! To draft an issue based on your patch run
npx patch-package @vant/cli --create-issue
最终在工程(B)根目录下,生成工程(B)/patches/@vant+cli+5.1.0.patch
补丁文件,将它提交到git中。
补丁原理也不复杂,其实就是一些git diff
记录描述:patch-package
会将当前node_modules
下的源码与原始源码进行git diff
,并在项目根目录下生成一个patch
补丁文件。以我当前的为例:
diff --git a/node_modules/@vant/cli/site/index.html b/node_modules/@vant/cli/site/index.html
index 5764eed..e32fb9e 100644
--- a/node_modules/@vant/cli/site/index.html
+++ b/node_modules/@vant/cli/site/index.html
@@ -33,5 +33,8 @@
<body>
<div id="app"></div>
<script type="module" src="/desktop/main.js"></script>
+ <script>
+ window.localStorage.setItem("vantTheme", "light")
+ </script>
</body>
</html>
diff --git a/node_modules/@vant/cli/site/mobile.html b/node_modules/@vant/cli/site/mobile.html
index e5ff77b..084a028 100644
--- a/node_modules/@vant/cli/site/mobile.html
+++ b/node_modules/@vant/cli/site/mobile.html
@@ -42,5 +42,8 @@
<body>
<div id="app"></div>
<script type="module" src="/mobile/main.js"></script>
+ <script>
+ window.localStorage.setItem("vantTheme", "light")
+ </script>
</body>
</html>
2.执行补丁
基于以上操作,切回到目标项目工程(A),在package.json中添加命令如下:
- 安装patch-package工具
# 安装patch-package工具,以便管理和应用补丁 $ npm install --save-dev patch-package
- 创建补丁文件夹:在项目根目录下新建一个名为
patches
的文件夹,用于存放补丁文件 - 切回到目标项目工程(A),在
package.json
中添加命令{ "scripts": { "postinstall": "patch-package" // 会在包安装完成后执行 } }
- 执行
npm install
命令:postinstall
脚本确保每次执行pnpm install
或npm install
安装依赖后,自动应用存放在patches
文件夹中的补丁$ npm i install 依赖包 ... > @zto/mzui@3.1.1-beta.1 patch > patch-package patch-package 6.4.7 Applying patches... @vant/cli@5.1.0 ✔ ... patch-package finished.
- 验证补丁效果:重新运行项目,检查补丁是否成功应用,并确保业务运行正常
三、其他方式:修复特定问题
想要单纯修改npm
包还有其他方式
1.对于单文件
- 拷贝覆盖法
- 修改引用法
利用postinstall
勾子,执行cp 修改过的文件 ./node_modules/包名/原始文件
,最终node_modules
下的文件被覆盖成修改后的文件:
{
"scripts": {
// 即每次install包后用修改后文件覆盖原始文件
"postinstall": "cp ./patches/vant-cli/* ./node_modules/vant/cli/site"
}
}
配置一个webpack alias
别名,如**‘原始文件的引用路径’: ‘修改后文件的引用路径’**,使得最终修改后的文件被引用,如:
// vite 原理类似
resolve: {
alias: {
'antd/upload': path.resolve(__dirname, './patched/upload/*'),
}
}
2.多个文件/整体项目
- 直接使用完成的源码,不再通过npm包方式引用
- 修改后的源码,发布到私有的npm仓库上,供项目使用
四、补丁管理常见问题
patch-package
优势: 使用git diff来记录补丁,比重写一份源码的方法更节省空间、安全和便捷patch-package
可在日常开发中优雅的解决鱼和熊掌不可兼得的难题。但问题出现时,最好还是从官方渠道寻求解决方案,如提issue并关注版本更新和bug修复情况,以便及时更新或者移除补丁
1.补丁包的日常管理和自动化管理
补丁包的日常管理: 一定要遵循 及时移除补丁 的原则,一旦官方package
发布了修复版本,请立即移除旧的补丁,避免可能的未知影响。
如果你想实现自动化管理,请参考 cloud_notes 补丁的管理
2.首次构建失败
新增补丁在 “构建平台"首次构建时 可能会面临构建失败,可清除工作目录后重新尝试构建
3.某个package
,存在多个版本的补丁包文件时,如何patch?
实际场景,描述如下:
- 如果存在
@lianpf/umi
的多个版本补丁包:如@lianpf+umi+1.6.0.patch
、@lianpf+umi+1.5.3.patch
、@lianpf+umi+1.5.0.patch
等 - 且以上补丁包都位于项目的
patches
目录下
那么在执行 "postinstall": "patch-package"
时,patch-package
将如何处理这些补丁包?
以上例子中,假设项目中安装 @lianpf/umi
的某个版本(例如 1.5.3
),则 结论如下:
patch-package
将只尝试应用@lianpf+umi+1.5.3.patch
。即只会针对已安装版本,尝试对应版本的补丁。- 如果不存在
1.5.3
版本的补丁,则不会应用任何补丁
所以,要 确保补丁文件与已安装的包版本相匹配 ,以避免潜在的错误和冲突。
补丁包匹配的原理和详细步骤如下(若不关注,则可直接忽略):
1.补丁扫描:patch-package
会扫描patches
目录下的所有.patch
文件,并识别出每个补丁文件的目标包名和版本号
2.对每个补丁文件,进行版本匹配:patch-package
会检查node_modules
中是否存在与【补丁文件中指定的版本号】相匹配的【包】
3.补丁应用:
- 找到与补丁文件中指定的版本号相匹配的包:
patch-package
会尝试将该补丁应用到该包上 - 没有找到完全匹配的版本号,但找到了一个可以“向下兼容”的较低版本:(例如,安装了
@lianpf/umi@1.6.0
但存在@lianpf+umi+1.5.3.patch
),patch-package
通常不会自动应用该补丁(因为补丁是为特定版本设计的,并且可能包含仅适用于该版本的更改)。但是,如果项目配置或patch-package
的某个特定版本允许这种“宽松”的匹配(这在标准行为中是不常见的),则可能会尝试应用补丁,但结果可能是不确定的。
4.处理多个补丁:
- 如果存在某个包的多个版本补丁文件,并且项目中安装了与这些版本之一相匹配的包,则
patch-package
将只尝试应用与已安装版本相匹配的补丁。
5.错误和警告:
- 指出补丁未被应用: 尝试应用一个与任何已安装包版本都不匹配的补丁,
patch-package
可能会记录一个错误或警告,指出补丁未被应用。 - 错误记录: 尝试应用补丁时遇到任何问题(如文件冲突、语法错误等),
patch-package
会记录相应的错误或警告。
参考
最后, 希望大家早日实现:成为编程高手的伟大梦想!
欢迎交流~
本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!