這篇文章主要介紹了如何提高React界面性能,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
總的說來,React是通過維護視圖中的內存(in-memory)模型來運作的。這通常被稱為虛擬DOM,它可以被用來確定實際DOM何時需要被更新。不過,由于操控實際DOM的成本較高,因此我們需要確保僅在必要時才去更新DOM,進而提高整體性能。
為了從React框架中獲取最高性能,并提升React界面的響應效率,本文將從各種功能函數(如Suspense)、以及基于類的組件出發,和您討論十項常用的、面向DOM的技術與方法。
在編寫基于類的組件時,您可以重寫shouldComponentUpdate()的生命周期方法。該方法的目的在于:明確地聲明目標組件是否需要被重新渲染(re-rendering)。值得注意的是,在更新實際DOM的生命周期中,渲染的開銷是非常巨大的。因此,只有在組件的屬性(props)或狀態(states)發生變化時,我們才需要讓React執行渲染。有時您甚至可以跳過渲染,以避免整體調用所產生的開銷。
shouldComponentUpdate的簽名和操作比較簡單。在如下簡單示例中,組件需要知曉應該在何種指定觸發條件下,去執行更新。該方法將接收到的屬性與狀態當作參數,如果返回為true,組件將執行渲染,否則并不觸發渲染。
shouldComponentUpdate(nextProps, nextState) { if (this.props.significant !== nextProps.significant) { return true; } return false; }
雖然上述代碼段主要檢查的是屬性,但是它對于狀態也同樣適用。當然,在實際應用中,對于屬性或狀態的檢查,并判定是否返回true,可能會更加復雜。如果您需要比較某個簡單的淺層值(shallow value),那么請使用下一個技巧--PureComponent。
如果您的組件僅需要對屬性和狀態進行簡單的淺層比較(shallow comparison,https://stackoverflow.com/a/5703797/467240),以確定是否需要渲染,那么完全可以使用PureComponent之類的擴展基類--class MyComponent extends React.PureComponent。它可以實現:當通過淺層比較,并未發現屬性或狀態發生了任何變化時,render()就不會被調用。顧名思義,PureComponent表示:僅在屬性或狀態改變時,才會觸發輸出的更改,因此該組件是純凈的,不會帶有任何副作用。
前面的技巧僅適用于那些基于類的組件。為了達到與常規功能性組件相似的效果,您可以使用useEffect hook和memo之類的功能性組件。其中,useEffect與shouldComponentUpdate有著相似的效果,它允許用戶指定:僅在某些變量發生更改的情況下,生效某種特定的功能,從而避免了整體變更的開銷。下面是一個簡單的useEffect示例:
const MyComponent = (props) => { useEffect(() => { console.info("Update Complete: " + props.significantVariable); }, [props.significantVariable]); }
由上述代碼段可知,如果props.significantVariable已被更改(即變量發生了變化),那么該代碼就會運行生效。
作為一個高階組件,memo包裝了各種組件,并擴展了它們的行為能力。也就是說,如果功能性組件具有相同的屬性,memo便能夠以緩沖的方式,“記住”它們的結果。據此,它可以有效地防止功能性組件在無視屬性是否一致的情況下,去盲目地執行渲染。
為了模仿PureComponent只關注屬性的行為,我們可以使用如下代碼段來包裝某些功能性組件,使其只檢查屬性的更改,而非狀態。由于屬性和狀態是不同的,因此通過比較,一旦props.quote被認定為未發生改變,則其對應的組件也不會重新渲染。
const MyComponent = (props) => { return <span>props.quote</span> } export default React.memo(SomeComponent)
同時,React.memo 可以通過第二個參數,來檢查函數的等效性:
export default React.memo(MyComponent, (oldProps, newProps) => {} );
通過上述代碼,我們可以實現對用例新的和舊的屬性進行比較。如果屬性相等,該函數則返回true。值得注意的是,這與我們在前面介紹的shouldComponentUpdate,在發現組件出現更新時返回true,正好相反。
現在,讓我們將注意力轉移到一項同時適用于功能性和類組件的技術--窗口化(windowing)上。例如有一個具有數千行記錄的數據表或列表,如果您想在該表所對應的應用界面上顯示大量數據集,那么就需要采用“窗口化”的方式來查詢數據。也就是說,我們可以通過一次性僅加載和顯示部分數據的形式,防止大量數據“卡死”應用的用戶界面(UI)。為此,我們時??梢杂玫絩eact-window庫(請參見--https://github.com/bvaughn/react-window)。
如果您覺得函數調用的成本過高,那么可以考慮對其進行緩存。如果各個參數相同,而且緩存能夠返回結果,我們就可以使用存儲式緩存(memorized cache)的方式,來避免各種針對數據獲取的調用。當然,函數緩存是否真的適用,還取決于函數的具體特征。
所謂延遲加載是指:我們僅在必要時,才去加載數據。React 16.6引入了React.lazy(),它允許用戶對代碼按需進行拆分。這意味著,您可以在使用常規組件語法的同時,獲得各種延遲加載的語義。
當然,React 16.6之前的版本,并非無法實現代碼拆分,只是在處置大型代碼庫時,相對比較繁瑣。
作為React 16的一項最顯著的新功能,并發模式可以讓用戶通過使用Suspense組件,實現數據獲取和渲染的并行處理,進而極大地提高應用程序的實際感知性能。
我們除了能夠用Suspense組件來定義數據的獲取區域之外,還可以使用諸如useDeferredValue等由React 16帶來的新組件,來提升自動建議(auto-suggest)等工作方式,進而避免用戶碰到諸如錯誤性的鍵入等不良的體驗。
大多數情況下,我們可以通過debounce或throttle函數,來更好地處理React的并發模式。如果您的代碼庫被鎖定為舊版的渲染引擎,而無法開啟并發模式時,此類函數便可以有效地避免在數據獲取的過程中,出現混亂的局面。
例如,如果您想在用戶鍵入數據的同時,實時地獲取他們的輸入,那么由于每個擊鍵都會觸發一個請求,因此整體的性能會大打折扣。對此,我們便可以使用debounce或throttle函數,來緩解此類問題。
除了上面提到的技術,我們還可以通過對應用程序進行性能分析,來獲悉性能瓶頸的所在,并驗證上述改進方法的實際效果。目前,像Chrome和Firefox之類的瀏覽器,都帶有內置的分析器(profiler)。一旦啟用了React的開發模式(dev mode),您將可以通過分析器,來查看某些正在使用的特定組件。這對于檢查網絡的狀態,以及識別后端調用的延遲,都是非常實用的。據此,您可以清晰地判斷出,到底是前端JavaScript的代碼問題,還是存在著需要后端修復的缺陷。
此外,React 16.5以及更高的版本,還提供了一個名為DevTools Profiler(https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)的工具。它既能夠為處于并發模式的各個函數,提供了更加詳盡的服務功能與集成;又可以通過多種方法,對應用程序的行為活動進行切片(slice)和切塊(dice)。
另一類Profiler組件則能夠展現組件渲染生命周期中的各種詳細信息。
最后,在部署生產環境時,您還需要確保生產環境構建(production build)的精簡性、且不包含任何開發調試過程中的日志記錄。當然,具體步驟取決于您所使用的構建工具。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“如何提高React界面性能”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。