Ant Design 的Bug修復(fù)示例詳解
引言
我在工作中大量使用Ant Design,它為我省去了很多重復(fù)勞動,所以有空的時候,我也會為Ant Design做一些微小的貢獻。
本文詳細描述了我處理一個Ant Design的Bug全過程,文章內(nèi)容較多但難度并不高,新手同學(xué)看完也可以嘗試為Ant Design添磚加瓦!
第一步、選擇要修復(fù)的Bug
Ant Design有不少Bug,你可以直接訪問它的Issues,挑一個簡單的問題嘗試解決,需要注意的是,并不是所以Issue都是Bug,經(jīng)過管理員確認過的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)容進行復(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默認導(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的日歷控件點擊輸入框右側(cè)的icon沒有彈出日歷面板的問題。
知道它的作用了,已經(jīng)成功了一半!看看要怎么修復(fù)?
第四步、問題修復(fù)
到這一步,建議先到對應(yīng)的Issue下面留言說明正在處理中,避免其他開發(fā)者再花時間修復(fù)同一個問題。
接著,要解決2個問題:
- 點擊日歷控件輸入框右側(cè)的icon需要打開日歷面板
- 不要用mouseUp事件來打開,想想其它實現(xiàn)方式
答案顯而易見了,那就是改成在onClick里觸發(fā),因為icon節(jié)點同樣被輸入框的父級包裹,icon的click事件必然會冒泡到父級,從而觸發(fā)父級的onClick。
查看以上源碼發(fā)現(xiàn),輸入框的父級已經(jīng)有一個onClick屬性,只需要將以上的4行代碼挪到onClick的回調(diào)即可,點擊這里查看詳細修改。
修改完成,查看本地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到遠程倉庫,打開倉庫頁面,會看到頁面上出現(xiàn)了一個【compare & pull request】的入口,點擊即可向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ù)示例詳解的詳細內(nèi)容,更多關(guān)于Ant Design Bug修復(fù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Canvas如何判斷點在形狀內(nèi)及內(nèi)置API性能詳解
這篇文章主要為大家介紹了Canvas如何判斷點在形狀內(nèi)及內(nèi)置API性能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03TypeScript實用技巧?Nominal?Typing名義類型詳解
這篇文章主要為大家介紹了TypeScript實用技巧?Nominal?Typing名義類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪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