Ant Design 的Bug修復(fù)示例詳解
引言
我在工作中大量使用Ant Design,它為我省去了很多重復(fù)勞動,所以有空的時候,我也會為Ant Design做一些微小的貢獻(xiàn)。
本文詳細(xì)描述了我處理一個Ant Design的Bug全過程,文章內(nèi)容較多但難度并不高,新手同學(xué)看完也可以嘗試為Ant Design添磚加瓦!
第一步、選擇要修復(fù)的Bug
Ant Design有不少Bug,你可以直接訪問它的Issues,挑一個簡單的問題嘗試解決,需要注意的是,并不是所以Issue都是Bug,經(jīng)過管理員確認(rèn)過的Bug一般會打上Bug的標(biāo)記。
這次我處理的問題是#37165。
第二步、準(zhǔn)備工作
Fork ant-design倉庫到自己的Github,回到自己的Github,將剛剛Fork的倉庫克隆到本地,然后基于master分支新建一個bugfix分支,接著安裝依賴,啟動項目,然后就可以在開發(fā)環(huán)境訪問Ant Design官網(wǎng)上所有的Demo,方便調(diào)試。
npm install && npm run start
第三步、問題排查
回到問題#37165,它的表現(xiàn)是:一個DatePicker,一個Input, 用戶用鼠標(biāo)選中Input的內(nèi)容進(jìn)行復(fù)制時經(jīng)常會滑到DatePicker,會觸發(fā)日歷控件輸入框的focus事件。
這種操作是比較常見的,問題簡化完了就是:只要在日歷控件上觸發(fā)mouseUp就會打開日歷控件。
查看官網(wǎng)Demo發(fā)現(xiàn)確實存在這個問題,第一反應(yīng)是日歷控件的輸入框綁定了onMouseUp事件,查看對應(yīng)的DatePicker組件源碼,定位到文件ant-design/components/date-picker/generatePicker/generateSinglePicker.tsx第121~156行
(基于antd@4.23.4版本,不同版本行數(shù)可能不同):
import RCPicker from 'rc-picker';
// 中間代碼省略……
<RCPicker<DateType>
ref={innerRef}
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
suffixIcon={suffixNode}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
dropdownClassName={popupClassName || dropdownClassName}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
{...additionalProps}
{...restProps}
{...additionalOverrideProps}
locale={locale!.lang}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(
prefixCls as string,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
className,
)}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
disabled={mergedDisabled}
/>
以上源碼沒有傳入onMouseUp,將解構(gòu)賦值prop的{...restProps}注釋掉,查看本地Demo,問題依然存在,說明restProps也沒有傳入onMouseUp。
由于DatePicker組件是基于RCPicker封裝的,接下來繼續(xù)深入RCPicker組件,看看onMouseUp是不是在RCPicker里面綁定的。
rc-picker是react-components組件庫中的日歷組件,Ant Design大部分組件都是在react-components基礎(chǔ)上封裝的。
查看rc-picker源碼,rc-picker默認(rèn)導(dǎo)出的是Picker組件:
import Picker from './Picker'; //... export default Picker;
定位到picker/src/Picker.tsx,搜索字符串"onMouseUp",發(fā)現(xiàn)在日歷控件輸入框的父級div綁定了onMouseUp事件,對應(yīng)的回調(diào)函數(shù)在287行(基于rc-picker@2.6.10):
// 輸入框父級元素 L530~L555
<div
ref={containerRef}
className={classNames(prefixCls, className, {
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-focused`]: focused,
[`${prefixCls}-rtl`]: direction === 'rtl',
})}
style={style}
onMouseDown={onMouseDown}
// 看這里
onMouseUp={onInternalMouseUp}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onContextMenu={onContextMenu}
// 順便關(guān)注一下這里,后文會說
onClick={onClick}
>
<div
className={classNames(`${prefixCls}-input`, {
[`${prefixCls}-input-placeholder`]: !!hoverValue,
})}
ref={inputDivRef}
>
{inputNode}
{suffixNode}
{clearNode}
</div>
</div>
// onMouseUp回調(diào) L287~L296
const onInternalMouseUp: React.MouseEventHandler<HTMLDivElement> = (...args) => {
if (onMouseUp) {
onMouseUp(...args);
}
// 問題就在這里
if (inputRef.current) {
inputRef.current.focus();
triggerOpen(true);
}
};
就在函數(shù)onInternalMouseUp中,除了觸發(fā)onMouseUp,還觸發(fā)了輸入框的focus事件!
if (inputRef.current) {
inputRef.current.focus();
triggerOpen(true);
}
此時,應(yīng)該弄清楚一件事:當(dāng)時為什么要寫這幾行代碼?
最好的途徑就是看看當(dāng)時的commit message,為了方便后續(xù)調(diào)試,還是將rc-picker倉庫克隆到本地:先Fork,再新建分支,然后安裝依賴,啟動項目。
PS:VSCode安裝GitLens插件,即可查看每一行代碼的commit記錄。
從這幾行代碼對應(yīng)的commit了解到,這幾行代碼是為了解決Ant Design的日歷控件點(diǎn)擊輸入框右側(cè)的icon沒有彈出日歷面板的問題。
知道它的作用了,已經(jīng)成功了一半!看看要怎么修復(fù)?
第四步、問題修復(fù)
到這一步,建議先到對應(yīng)的Issue下面留言說明正在處理中,避免其他開發(fā)者再花時間修復(fù)同一個問題。
接著,要解決2個問題:
- 點(diǎn)擊日歷控件輸入框右側(cè)的icon需要打開日歷面板
- 不要用mouseUp事件來打開,想想其它實現(xiàn)方式
答案顯而易見了,那就是改成在onClick里觸發(fā),因為icon節(jié)點(diǎn)同樣被輸入框的父級包裹,icon的click事件必然會冒泡到父級,從而觸發(fā)父級的onClick。
查看以上源碼發(fā)現(xiàn),輸入框的父級已經(jīng)有一個onClick屬性,只需要將以上的4行代碼挪到onClick的回調(diào)即可,點(diǎn)擊這里查看詳細(xì)修改。
修改完成,查看本地Demo,問題已經(jīng)解決了,勝利在望!
第五步、單元測試
原來的commit寫了對應(yīng)的單元測試,我們修改了它的實現(xiàn),同時也需要修改對應(yīng)的單元測試:
// picker/tests/picker.spec.tsx L620~631
it('Picker should open when click inside', () => {
const onClick = jest.fn();
const wrapper = mount(<MomentPicker onClick={onClick} />);
const inputElement = wrapper.find('input').instance() as any as HTMLInputElement;
inputElement.focus = jest.fn();
wrapper.find('.rc-picker').simulate('click');
expect(inputElement.focus).toHaveBeenCalled();
expect(wrapper.isOpen()).toBeTruthy();
expect(onClick).toHaveBeenCalled();
});
修改完成之后,完整地跑一遍單元測試:
npm run test
確保所有單元測試都通過
Test Suites: 8 passed, 8 total
Tests: 294 passed, 294 total
Snapshots: 11 passed, 11 total
Time: 15.643s
代碼部分到這里就算圓滿完成了!
第六步、提交Pull Request
這一步,是本文最簡單的一步了!
提交代碼,push到遠(yuǎn)程倉庫,打開倉庫頁面,會看到頁面上出現(xiàn)了一個【compare & pull request】的入口,點(diǎn)擊即可向Ant Design創(chuàng)建合并請求,輸入修復(fù)的Issue鏈接(這個很重要,到時候Issue頁面會自動關(guān)聯(lián)你的此次的pull request)和修復(fù)思路或理由,方便管理員review。
最后,等待管理員review,沒問題的話管理員會合并分支,然后發(fā)布新版本,代碼最終會應(yīng)用到數(shù)十萬項目中,Bug修復(fù)大功告成!
總結(jié)
最后,總結(jié)一下,第一次提交PR,建議選擇簡單的Issue,重要的是先將整個流程熟悉一遍,不要一開始就挑戰(zhàn)復(fù)雜的問題,如果花了很長時間卻解決不了問題,自信心受挫,可能就不想繼續(xù)下去了。
以上就是Ant Design 的Bug修復(fù)示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Ant Design Bug修復(fù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Canvas如何判斷點(diǎn)在形狀內(nèi)及內(nèi)置API性能詳解
這篇文章主要為大家介紹了Canvas如何判斷點(diǎn)在形狀內(nèi)及內(nèi)置API性能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
TypeScript實用技巧?Nominal?Typing名義類型詳解
這篇文章主要為大家介紹了TypeScript實用技巧?Nominal?Typing名義類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
微信小程序通過api接口將json數(shù)據(jù)展現(xiàn)到小程序示例
這篇文章主要介紹了微信小程序通過api接口將json數(shù)據(jù)展現(xiàn)到小程序示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01
微信小程序(應(yīng)用號)簡單實例應(yīng)用及實例詳解
這篇文章主要介紹了微信小程序(應(yīng)用號)簡單實例應(yīng)用的相關(guān)資料,需要的朋友可以參考下2016-09-09

