官方介紹
patch-package 給開發(fā)者提供了通過打“補(bǔ)丁”的方式,使得重新安裝依賴包時能夠保留之前對第三方依賴包的修改的一種解決方案。
應(yīng)用場景
我們在使用第三方依賴包時如果遇到了 bug,通常解決的方式都是繞過這個問題,使用其他方式解決,較為麻煩?;蛘呓o作者提個 issue 或者 PR,然后等待作者的修復(fù)。等待的時間不可控,此時就可以借助 patch-package 自己動手去修復(fù)該 bug,感覺是不是很棒。并且還可以在第三方依賴包上,根據(jù)業(yè)務(wù)需求擴(kuò)展能力。
最好還是擴(kuò)展一些通用性比較高的能力,如果是比較通用且該能力大多數(shù)開發(fā)者都有這種訴求的話可以給第三方依賴包提個 PR。
使用方法
安裝
都是在項目里自行安裝
npm
npm i patch-package
yarn
yarn add patch-package postinstall-postinstall
為什么 yarn 要添加 postinstall-postinstall 包
yarn 在 yarn、yarn install 和 yarn add <package> 之后運(yùn)行 postinstall 腳本,但在 yarn remove <package> 之后不運(yùn)行。如果您將此包添加到您的項目中,即使在 yarn remove <package> 之后,它也會執(zhí)行您項目的 postinstall 鉤子。這需要你的 postinstall 腳本是冪等的,因為它會為 yarn、yarn install 和 yarn add <package> 運(yùn)行兩次
修改 package.json
依賴包在安裝完之后會執(zhí)行 postinstall 命令
"scripts": {
***,
+ "postinstall": "patch-package"
}
修改依賴包源碼
生成補(bǔ)丁
yarn patch-package package-name(修改的包名)
# 或者
npx patch-package package-name(npm版本 > 5.2)
執(zhí)行npx patch-package antd結(jié)果
npx patch-package antd
Need to install the following packages:
patch-package
Ok to proceed? (y) y
patch-package 6.4.7
? Creating temporary folder
? Installing antd@4.17.3 with yarn
? Diffing your files with clean files
? Created file patches/antd+4.17.3.patch
?? antd is on GitHub! To draft an issue based on your patch run
yarn patch-package antd --create-issue
可以看到patch-package已經(jīng)為我們創(chuàng)建了一個補(bǔ)丁。
默認(rèn)會在我們的根目錄下創(chuàng)建一個patches文件夾。在patches文件夾下會創(chuàng)建依賴包名+版本號.patch的文件,文件描述了我們修改了什么,第幾行,有點像git的提交記錄。
diff --git a/node_modules/antd/lib/button/button.js b/node_modules/antd/lib/button/button.js
index da5fc9a..7eec125 100644
--- a/node_modules/antd/lib/button/button.js
+++ b/node_modules/antd/lib/button/button.js
@@ -143,40 +143,40 @@ var InternalButton = function InternalButton(props, ref) {
var _classNames;
var _props$loading = props.loading,
- loading = _props$loading === void 0 ? false : _props$loading,
- customizePrefixCls = props.prefixCls,
- type = props.type,
- danger = props.danger,
- _props$shape = props.shape,
- shape = _props$shape === void 0 ? 'default' : _props$shape,
- customizeSize = props.size,
- className = props.className,
- children = props.children,
- icon = props.icon,
- _props$ghost = props.ghost,
- ghost = _props$ghost === void 0 ? false : _props$ghost,
- _props$block = props.block,
- block = _props$block === void 0 ? false : _props$block,
- _props$htmlType = props.htmlType,
- htmlType = _props$htmlType === void 0 ? 'button' : _props$htmlType,
- rest = __rest(props, ["loading", "prefixCls", "type", "danger", "shape", "size", "className", "children", "icon", "ghost", "block", "htmlType"]);
+ loading = _props$loading === void 0 ? false : _props$loading,
+ customizePrefixCls = props.prefixCls,
+ type = props.type,
+ danger = props.danger,
+ _props$shape = props.shape,
+ shape = _props$shape === void 0 ? 'default' : _props$shape,
+ customizeSize = props.size,
+ className = props.className,
+ children = props.children,
+ icon = props.icon,
+ _props$ghost = props.ghost,
+ ghost = _props$ghost === void 0 ? false : _props$ghost,
+ _props$block = props.block,
+ block = _props$block === void 0 ? false : _props$block,
+ _props$htmlType = props.htmlType,
+ htmlType = _props$htmlType === void 0 ? 'button' : _props$htmlType,
+ rest = __rest(props, ["loading", "prefixCls", "type", "danger", "shape", "size", "className", "children", "icon", "ghost", "block", "htmlType"]);
var size = React.useContext(_SizeContext["default"]);
var _React$useState = React.useState(!!loading),
- _React$useState2 = (0, _slicedToArray2["default"])(_React$useState, 2),
- innerLoading = _React$useState2[0],
- setLoading = _React$useState2[1];
+ _React$useState2 = (0, _slicedToArray2["default"])(_React$useState, 2),
+ innerLoading = _React$useState2[0],
+ setLoading = _React$useState2[1];
var _React$useState3 = React.useState(false),
- _React$useState4 = (0, _slicedToArray2["default"])(_React$useState3, 2),
- hasTwoCNChar = _React$useState4[0],
- setHasTwoCNChar = _React$useState4[1];
+ _React$useState4 = (0, _slicedToArray2["default"])(_React$useState3, 2),
+ hasTwoCNChar = _React$useState4[0],
+ setHasTwoCNChar = _React$useState4[1];
var _React$useContext = React.useContext(_configProvider.ConfigContext),
- getPrefixCls = _React$useContext.getPrefixCls,
- autoInsertSpaceInButton = _React$useContext.autoInsertSpaceInButton,
- direction = _React$useContext.direction;
+ getPrefixCls = _React$useContext.getPrefixCls,
+ autoInsertSpaceInButton = _React$useContext.autoInsertSpaceInButton,
+ direction = _React$useContext.direction;
var buttonRef = ref || /*#__PURE__*/React.createRef();
var delayTimeoutRef = React.useRef();
@@ -218,10 +218,11 @@ var InternalButton = function InternalButton(props, ref) {
React.useEffect(fixTwoCNChar, [buttonRef]);
var handleClick = function handleClick(e) {
+ console.log(234567) // 我的修改
var _a;
var onClick = props.onClick,
- disabled = props.disabled; // https://github.com/ant-design/ant-design/issues/30207
+ disabled = props.disabled; // https://github.com/ant-design/ant-design/issues/30207
if (innerLoading || disabled) {
e.preventDefault();
測試補(bǔ)丁是否有效
- 手動刪除node_modules文件夾,重新執(zhí)行yarn安裝依賴包??梢钥吹皆谝蕾嚢惭b結(jié)束后執(zhí)行了patch-package命令,之前生成的補(bǔ)丁被應(yīng)用了。因為我們配置了postinstall腳本,所以會自動執(zhí)行patch-package命令
$ patch-package
patch-package 6.4.7
Applying patches...
antd@4.17.3 ?
? Done in 3.32s.
- 查看node-modules中之前修改的antd修改的地方,查看之前修改的代碼是否還存在。如果之前修改的代碼還存在,說明補(bǔ)丁文件已經(jīng)生效了,如果不存在,排查下是否哪個步驟出現(xiàn)了問題。
antd/lib/button/button.js
var handleClick = function handleClick(e) {
console.log(234567) // Ops! 存在
var _a;
var onClick = props.onClick,
disabled = props.disabled; // https://github.com/ant-design/ant-design/issues/30207
if (innerLoading || disabled) {
e.preventDefault();
return;
}
(_a = onClick) === null || _a === void 0 ? void 0 : _a(e);
};
最后將patches文件夾推送到遠(yuǎn)端倉庫,日后無論是誰拉取代碼,安裝依賴,我們之前修改的部分都會生效的
注意事項
patch是鎖定版本號的,如果升級了版本,patch內(nèi)容將會失效,最好在package.json能夠鎖定版本號。
修改的同時,也局限了升級的能力,盡量還是去提issue和PR。