企業(yè)網(wǎng)站 wordpress網(wǎng)頁代碼模板
三個(gè)可以優(yōu)化的地方
避免過度多次渲染?
組件會(huì)在以下情況下重新渲染
注意:例如組件組合的形式,<Test><Counter></Counter></Test>,即使Test發(fā)生了重新渲染,Counter也不會(huì)重新渲染。另外使用React這樣的庫或框架時(shí),渲染指的是組件函數(shù)被調(diào)用的過程。這個(gè)過程并不一定意味著文檔對(duì)象模型(DOM)實(shí)際上被更新了。它只是意味著組件的邏輯被執(zhí)行,可能會(huì)重新計(jì)算組件的狀態(tài)和屬性。
?什么是過度渲染呢?
- 無效渲染是指組件函數(shù)被調(diào)用了,但是并沒有導(dǎo)致DOM的任何變化。
- 這通常發(fā)生在組件的狀態(tài)或?qū)傩詻]有改變,但組件仍然被重新渲染的情況下。這種渲染是不必要的,因?yàn)樗牧速Y源但沒有帶來任何視覺上的變化。
- 當(dāng)然這在react中大部分情況下是沒問題的,只有這種情況過度頻繁時(shí)才會(huì)產(chǎn)生問題。
記憶化
組件、對(duì)象、函數(shù)都可以記憶化。
?memo
工作原理
-
創(chuàng)建不會(huì)重新渲染的組件:
memo
用于創(chuàng)建一個(gè)組件,當(dāng)其父組件重新渲染時(shí),如果傳入的props在兩次渲染之間沒有變化,那么這個(gè)被memo
包裹的組件將不會(huì)重新渲染。
-
只影響props:
memo
只對(duì)組件的props進(jìn)行比較。如果一個(gè)被memo
包裹的組件在props沒有變化的情況下,即使它自己的state發(fā)生了變化,或者它訂閱的context發(fā)生了變化,它仍然會(huì)重新渲染。
局限性
-
在React中,每次渲染都會(huì)重新創(chuàng)建所有內(nèi)容(包括對(duì)象和函數(shù)): 這意味著,即使組件的狀態(tài)沒有改變,每次渲染時(shí),組件內(nèi)部的函數(shù)和對(duì)象都會(huì)被重新實(shí)例化。
-
在JavaScript中,兩個(gè)看起來相同的對(duì)象或函數(shù)實(shí)際上是不同的({} != {}): 在JavaScript中,對(duì)象和函數(shù)是引用類型。即使兩個(gè)對(duì)象的內(nèi)容完全相同,它們?cè)趦?nèi)存中的地址也是不同的。這意味著,即使兩個(gè)對(duì)象看起來一樣,它們也是兩個(gè)不同的實(shí)例。
-
因此,如果將對(duì)象或函數(shù)作為props傳遞,子組件在每次重新渲染時(shí)都會(huì)將它們視為新的props: 當(dāng)父組件將對(duì)象或函數(shù)作為props傳遞給子組件時(shí),由于每次渲染都會(huì)創(chuàng)建新的實(shí)例,子組件會(huì)認(rèn)為這些props是新的,即使它們的值沒有變化。
-
如果props在重新渲染之間不同,memo將不起作用:
memo
是React的一個(gè)優(yōu)化技巧,它通過比較props來決定是否重新渲染組件。如果props在兩次渲染之間沒有變化,memo
可以防止不必要的渲染。但是,如果props是對(duì)象或函數(shù),即使它們的值沒有變化,由于它們是新創(chuàng)建的實(shí)例,memo
也會(huì)認(rèn)為props已經(jīng)改變,從而導(dǎo)致子組件重新渲染。
所以,我們需要對(duì)對(duì)象和函數(shù)進(jìn)行memoization(記憶化),以使它們?cè)谥匦落秩局g保持穩(wěn)定(保持不變)。
useMemo與useCallback
工作原理
-
用于記憶化(memoize)對(duì)象和函數(shù)
-
緩存機(jī)制:
- 當(dāng)你將對(duì)象或函數(shù)傳遞給?
useMemo
?或?useCallback
?時(shí),這些值或函數(shù)會(huì)被存儲(chǔ)在內(nèi)存中(即被“緩存”)。在隨后的重新渲染中,只要依賴項(xiàng)(即“輸入”)沒有變化,就會(huì)返回緩存的值或函數(shù)。
- 當(dāng)你將對(duì)象或函數(shù)傳遞給?
-
依賴數(shù)組:
useMemo
?和?useCallback
?都接受一個(gè)依賴數(shù)組,這與?useEffect
?的工作方式類似。依賴數(shù)組用于追蹤哪些值或狀態(tài)影響到了記憶化的值或函數(shù)。- 如果依賴數(shù)組中的任何一個(gè)值發(fā)生變化,那么記憶化的值或函數(shù)就會(huì)被重新創(chuàng)建。這是通過比較新舊依賴項(xiàng)來實(shí)現(xiàn)的,只有當(dāng)依賴項(xiàng)發(fā)生變化時(shí),才會(huì)觸發(fā)重新計(jì)算。
?使用場(chǎng)景
-
防止不必要的渲染:
- 通過與?
React.memo
?一起使用,可以記憶化 props,以避免不必要的組件重新渲染。
- 通過與?
-
避免每次渲染時(shí)的昂貴重新計(jì)算:
- 使用?
useMemo
?來記憶化那些計(jì)算成本較高的值。這樣,只有在其依賴項(xiàng)發(fā)生變化時(shí),才會(huì)重新計(jì)算這些值,從而節(jié)省性能。
- 使用?
-
記憶化在其他 Hook 的依賴數(shù)組中使用的值:
- 當(dāng)某個(gè)值被用作另一個(gè) Hook(如?
useEffect
)的依賴數(shù)組中的項(xiàng)時(shí),使用?useMemo
?或?useCallback
?來記憶化這個(gè)值可以避免無限循環(huán)的副作用。例如,如果你在?useEffect
?的依賴數(shù)組中直接使用一個(gè)會(huì)不斷變化的值,可能會(huì)導(dǎo)致無限循環(huán)的副作用執(zhí)行。通過記憶化這個(gè)值,你可以確保只有當(dāng)值實(shí)際改變時(shí),副作用才會(huì)重新運(yùn)行。
- 當(dāng)某個(gè)值被用作另一個(gè) Hook(如?
案例
假設(shè)我們?cè)貯pp.js中有以下對(duì)象和函數(shù),使用了useMemo與useCallback:
const [isFakeDark, setIsFakeDark] = useState(false);const [posts, setPosts] = useState(() =>Array.from({ length: 30 }, () => createRandomPost()));const handleAddPost = useCallback(function handleAddPost(post) {setPosts((posts) => [post, ...posts]);}, []);const archiveOptions = useMemo(() => {return {show: false,title: `Post archive in addition to ${posts.length} main posts`,};}, [posts.length]);<ArchivearchiveOptions={archiveOptions}onAddPost={handleAddPost}setIsFakeDark={setIsFakeDark}/>
注意下useMemo與useCallback使用方式的細(xì)微差別。
?Archive使用memo如下所示:
const Archive = memo(function Archive({ archiveOptions, onAddPost }) {// Here we don't need the setter function. We're only using state to store these posts because the callback function passed into useState (which generates the posts) is only called once, on the initial render. So we use this trick as an optimization technique, because if we just used a regular variable, these posts would be re-created on every render. We could also move the posts outside the components, but I wanted to show you this trick 😉const [posts] = useState(() =>// 💥 WARNING: This might make your computer slow! Try a smaller `length` firstArray.from({ length: 30000 }, () => createRandomPost()));const [showArchive, setShowArchive] = useState(archiveOptions.show);return (<aside><h2>{archiveOptions.title}</h2><button onClick={() => setShowArchive((s) => !s)}>{showArchive ? "Hide archive posts" : "Show archive posts"}</button>{showArchive && (<ul>{posts.map((post, i) => (<li key={i}><p><strong>{post.title}:</strong> {post.body}</p><button onClick={() => onAddPost(post)}>Add as new post</button></li>))}</ul>)}</aside>);
});
注意: 對(duì)于state的set函數(shù)來說,它不需要useCallback函數(shù),它本身隨著渲染就不會(huì)改變地址。
減小Bundle大小?
Bundle與Bundle分別是什么?
代碼分割
代碼分割(Code splitting)是一種在現(xiàn)代前端工程中常用的優(yōu)化技術(shù),它允許將應(yīng)用程序的代碼庫分割成多個(gè)小塊(bundles),這些小塊可以獨(dú)立于主代碼庫進(jìn)行加載。這種方法特別適用于大型應(yīng)用程序,可以顯著提高加載速度和性能
?路由級(jí)別代碼分割
轉(zhuǎn)變?yōu)?/p>
Suspense是一個(gè)用于處理異步組件加載的組件。它允許你定義一個(gè)邊界,當(dāng)其子組件在加載過程中遇到異步操作(如數(shù)據(jù)獲取)而暫停時(shí),可以顯示一個(gè)備用內(nèi)容(fallback)。然后Suspense與React的lazy
函數(shù)結(jié)合使用,實(shí)現(xiàn)組件的懶加載。這種方式允許你在組件首次渲染時(shí)自動(dòng)觸發(fā)組件的加載,同時(shí)在加載過程中顯示一個(gè)占位符。
因?yàn)閼屑虞d的元素,當(dāng)你切換到Login或者Pricing等頁面的時(shí)候,?對(duì)應(yīng)的js的代碼塊才剛剛從服務(wù)器下載過來,需要時(shí)間到達(dá)客戶端。這個(gè)時(shí)候suspense組件就起到了作用!