2020 轉眼已來到3月,但疫情的突襲,讓這個春節遲遲沒有開始,也無法結束。這段時間看似是充電自我提高的大好時光,但家國情懷深厚的我爲疫情真的是操碎了心,時不時都要看看哪裏數據猛增了,哪裏暴發了。結束一個月的在家辦公,帶着口罩在公司上班,狀態稍有好轉,注意力終歸回到了技術。 前端
最近在組裏推BFF Node接入與微前端改造,看到了組裏各式各樣實現地業務代碼(組件),有激進開放的整個頁面都用hooks實現,有沉迷於過去的停留於redux + saga + model的類組件寫法,固然更多的是類組件中的子組件參和幾個hooks。我帶着好奇之心去goggle了一下這兩個誰勝一籌,因而有了此文。react
網上看了不少大佬觀點,但脫離業務的爭論都是耍流氓。做爲眼見爲實躺坑無數的練習生,我決定用事實說話。說那麼多幹嗎,上代碼:
慢...,先說說接下來要乾的事情:git
發現沒,這就是一個標準的CRUD
,頁面大體長這樣:github
接下來,列一下兩種方式實現,頁面的大體結構,橙色方框內爲Hook重構後的頁面結構:
Hook重構後代碼變化主要在index.js,刪去了對Dva的Model依賴,轉由Hook本身管理數據, 代碼對比大體以下:
看代碼,主要是數據層的實現有變化,UI保持一致。當頁面跑起來,若是不看麪包屑的話,也很難分辨誰是Hooks組件的實現,誰是類組件的實現,因此表面的看,是看不出誰高誰低的。spring
畢竟人的肉眼,只有當低於30fps時,才能明顯的觀察到變化;因此要想客觀的對比性能,得依賴Chrome的性能分析(Performerce);這裏作兩個對比:chrome
跳轉到列表頁 redux
打開編輯頁 緩存
爲保證試驗結果嚴謹,我儘可能保證惟一性變量原則、多(san)次、與減小本身手抖的次數,但與自動化測試仍是有較大差距,十幾毫秒的偏差再所不免。我觀察了列表頁切換頁
的兩個圖,除開請求的波動於手動測試的偏差,兩個頁面從點擊到請求再到頁面渲染,時間相差無幾,甚至調用棧都是驚人的類似(裝逼的說法就是:從原理上分析,也應該是類似的:dispatch + diff + render)。
也觀察了詳情編輯Modal打開
兩個圖,屢次測試,其打開速度hooks稍暫上風,因爲這一塊的實現邏輯有比較大的差異(class
組件的數據獲取是在最外層,獲取完而後依次向裏傳遞;而hooks
則是從外層獲取到id後,組件內部直接發起請求獲取數據),因此二者火焰圖也有比較大的差別;但從頁面渲染的角度總體感受差異不大。 性能優化
最後我得出的見解是:Hooks更可能是一種管理數據的手段,與class相比,並無什麼性能上的優點,更多的主動權,在編寫代碼的人手裏,就像我駕校老師愛說的那句狗屁不通的諺語:再好的車,給這個二傻子開,都能開熄火。關於更多,能夠關注B乎討論: React hooks 和 Class Component 的性能哪個更好?。
若是對個人測試有疑惑,能夠本身動手,我提供示例項目:antd
我我的始終同意:框架只是實現業務的手段,在使用成熟的框架前提下,頁面的性能徹底由司機掌控。(我我的有個觀點就是:Vue是自動檔,React更像手動檔
)
要想徹底脫離redux或mobx,簡單使用Hooks中的useState或useEffect來完成頁面,其難度仍是很大且很難管理
,畢竟頁面大多數數據源都來自異步請求,因此封裝一個useRequest hooks是勢在必要的,並且Hooks最大的優點就是邏輯複用。如下將分享部門封裝useRequest組件的思考過程,其思路參考於Apollo-Graphql
的react-hooks項目。先看示例代碼(上面列表頁的部分代碼):
export default function Root() { const [search, onSearch, onReset] = usePagination({}); // 請求:admin.closertb.site/rule/query接口 const { data = {}, loading, error } = useRequest('/rule/query', search); const { datas, total } = data; const tableProps = { search, datas, fields, onSearch, total, loading, }; const searchProps = { fields: searchFields, search, onReset, onSearch, }; return ( <div> <WithSearch {...searchProps} /> <div className="pageContent"> <EnhanceTable {...tableProps} /> </div> </div> ); }
以上就是一個最多見的useRequest hook應用,用很是簡短的代碼替換了Dva中的路由監聽(subscription
), 異步請求(effect
),數據更新(reducer
)等一連串邏輯;下圖是一個簡單的流程示意圖:
根據這個示意圖,老司機應該就大概知道怎麼實現的了:
useRef
來緩存請求實例,即每一個useRequest僅建立一個query實例,頁面更新時,沿用已經存在的示例;useMemo
作計算,判斷請求參數是否更新;useEffect
, 用useMemo計算結果做爲依賴,判斷是否發起請求;如今留下的惟一疑問就是,發佈訂閱怎麼實現的,看一下代碼:
// query 示例其中的兩個核心方法: startQuery() { const { url, body, forceUpdate, result } = this; if (this.status > STATUS.fetch) { return; } // 狀態反轉爲請求中 this.status = STATUS.fetch; result.loading = true; // 發起請求 this.request(url, body).then((data) => { // 更新結果 result.data = data; result.loading = false; this.status = STATUS.success; // 更新訂閱 forceUpdate(); }).catch((error) => { this.status = STATUS.error; result.error = error; result.loading = false; forceUpdate(); }); } execute() { // skip:是否禁用查詢 const { result, options: { skip = false } } = this; !skip && this.startQuery(); // 當skip 爲true 時,說明沒有查詢結果,因此不能用上次的查詢結果來作過渡 return Object.assign({ error: undefined, data: undefined, loading: !skip, forceUpdate: this.forceQuery, }, skip ? {} : result); }
是否是有種恍然大悟,並無什麼發佈訂閱的具體實現,徹底依賴於Promise
對象隱式的發佈訂閱
,而forceUpdate的實現則依賴於useReducer
:
const [tick, forceUpdate] = useReducer(x => x + 1, 0);
從去年用Graphql
寫完本身的博客,發現Apollo的useQuery這個hooks彩蛋,就一直有想法去實現一個useRequest hooks,在去年的一次需求推動中,強迫本身去編寫了這個組件。收穫仍是很大的,在咱們團隊中已經有必定嘗試,固然還有很大的拓展空間。從這個組件的編寫歷程,我也再一次體驗了開手動擋,司機經驗的重要性,拿我本身挖到的一個坑舉例:
cleanup() { this.result.data = undefined; this.result.loading = false; this.result.error = undefined; }
上面一段代碼,是每次請求完成後,頁面更新後, 有一個useEffect反作用會執行query.cleanup()
來保證請求示例回到初始狀態。但就這樣一段代碼,引發了極大的性能問題:
事件喚起查詢時,頁面會抖動,開始我覺得是我寫的hooks的組件不如
react-redux
那麼多性能優化,其實當時在猜疑是否是hooks的性能問題。
但後面經過應用chrome performance,觀測到其抖動,是因爲this.result.data = undefined
形成的,用一個示意圖表示:
大概意思就是,若是當前頁面有數據,再次發起查詢時,列表除了當前更新爲loading狀態,當前10天數據也會被清除,至關於列表作了一次diff並render
;請求完成後,loading狀態消失,更新列表,又作了一次diff並render.因此會形成抖動。後面完善代碼後,就和下面正常的階躍曲線同樣,只有一次階躍,列表實際只會作一次render.
這次實現是基於團隊現有的http庫來作的,這個庫的原理在之前的一篇文章講過:邊看邊寫:基於Fetch仿洋蔥模型寫一個Http構造類
若是你感興趣,能夠在個人github看到:
沒啥想總結的,願疫情早日過去。願口罩早日摘下,願火鍋早日成爲生活的平常。
對了,Antd4.0已經到來,Form表單基本被重寫,這意味着我組件庫Antd-doddle, 又得作一次大的升級了!!!!cd