🎋 竹子日誌

關於 React 的效能

這個大概是萬年話題,許多人吵了翻天,大家經常為了「有沒有必要使用 useMemo 優化效能」而相互爭吵,但從來沒有人能夠具體提出一個「數字化」的說法去佐證這件事。為了驗證效能,我覺得具體的算法就是計算時間,我想到直接在 browser 上直接使用 console.time 來測試實際運算的時間。依照每個人的電腦效能不同,消耗掉的資源及運行速度也有所不同,所以我才用「比較」的方式來驗證這件事。

先量一下:十萬圈迴圈要跑多久

首先我先建立一個十萬量級的迴圈,當然我也知道 forEach 在跑這麼高量級時效能很差,你我就先算這個十萬圈跑完要多少時間。測試方式是在單純的 CRA 專案嘗試連續觸發 30次side effect。結果蠻訝異的,在我的電腦裡瀏覽器跑十萬圈花了 0.7~3.2 毫秒。以現代 120 幀的螢幕渲染的計算,每幀必須在8毫秒內完成,也就是說只要接近百萬迴圈量級就有可能效能瓶頸,而且不是每台電腦都可以有這麼快的運算速度。當然越小的迴圈會有越明顯基本開銷,這時實際能承受的迴圈數其實是更小的。

一個 useMemo 到底吃掉多少效能

接著就是我最好奇的,每一個 useMemo 到底消費了多少效能?接著我嘗試去壓測,一樣採十萬量級並一組依賴變數花費 7.8~2.1 毫秒,接著提高依賴變數數量到 10個變成12.9~29.3 毫秒,然後我再提高依賴到100個 22.3~67.7 毫秒。但實質上在於一個專案實在不會消耗這麼大的 useMemo 壓載,所以我覺得useMemo並沒有想像中的消耗效能。

建立 Virtual Node 比原生元素還貴

但我就想,大家說建立一個 Virtual Node 其實不耗效能,我嘗試不掛載純產生看有多大的消耗。一樣十萬量級,一個空的物件是 1.9 ~ 8.5 毫秒,document.createElement 建立 div 是 14.5 ~ 29.1毫秒,空的 jsx div 是 42.2 ~ 66.4毫秒,這又震碎我三觀了,如果在不掛載前提下純產生,jsx 產生的 Virtual Node 比原生元素更耗效能!!!而且建立 jsx 元素竟然比 useMemo 消耗大上數倍。

真正的元兇是掛載,也就是渲染

然後思考了一下,如果加上了掛載到底多耗效能呢?我嘗試插入產生出div 插入 body ,結果才1000筆就消耗掉 1.2 ~ 2.9ms 的運算時間,進入萬筆瀏覽器就直接躺平了…這概就是為什麼人家說 DOM 操作很耗效能的元兇,也就是渲染這件事。

與其吵要不要 memo,不如把數字量出來

有時工程師常常陷入「效能」議題,沒有辦法很好的陳述究竟為什麼要優化,而究竟為什麼不需要優化。數字化是一個很好的方式,可以很明確講述具體的績效與理由。自從我開始寫 React 經常聽到這種效能議題,一派贊成「不管什麼我都 memo 起來就對了」,另一派贊成「需要我再 memo,不需要我就不處理了」。或許檢查是消耗,生成也依然是消耗。妥善管理好節點樹的運算也是前端工程師的責任,找清楚原因和給予明確的指標,而非人云亦云。

當然這是一種作法,我覺得仍然可能有我沒有考慮清楚的地方,也希望有熟悉底層的大神能相互交流聊聊提供一點指教~~

留言