這一篇文章我們主要來學(xué)習(xí) React 的一些經(jīng)??嫉降幕A(chǔ)知識點, 注意:為了方便大伙閱讀理解,有些代碼塊采用截圖的形式
生命周期 在 V16 版本中引入了 Fiber 機制。這個機制一定程度上的影響了部分生命周期的調(diào)用,并且也引入了新的 2 個 API 來解決問題,關(guān)于 Fiber 的內(nèi)容將會下一篇文章的原理分析中講到。 在之前的版本中,如果你擁有一個很復(fù)雜的復(fù)合組件,然后改動了最上層組件的 state,那么調(diào)用??赡軙荛L
調(diào)用棧過長,再加上中間進行了復(fù)雜的操作,就可能導(dǎo)致長時間阻塞主線程,帶來不好的用戶體驗。Fiber 就是為了解決該問題而生。 Fiber 本質(zhì)上是一個虛擬的堆棧幀,新的調(diào)度器會按照優(yōu)先級自由調(diào)度這些幀,從而將之前的同步渲染改成了異步渲染,在不影響體驗的情況下去分段計算更新。
對于如何區(qū)別優(yōu)先級,React 有自己的一套邏輯。對于動畫這種實時性很高的東西,也就是 16 ms 必須渲染一次保證不卡頓的情況下,React 會每 16 ms(以內(nèi)) 暫停一下更新,返回來繼續(xù)渲染動畫。 對于異步渲染,現(xiàn)在渲染有兩個階段:reconciliation 和 commit 。前者過程是可以打斷的,后者不能暫停,會一直更新界面直到完成。 Reconciliation 階段 componentWillMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdate Commit 階段 componentDidMountcomponentDidUpdatecomponentWillUnmount 因為 Reconciliation 階段是可以被打斷的,所以 Reconciliation 階段會執(zhí)行的生命周期函數(shù)就可能會出現(xiàn)調(diào)用多次的情況,從而引起 Bug。由此對于 Reconciliation 階段調(diào)用的幾個函數(shù),除了 shouldComponentUpdate 以外,其他都應(yīng)該避免去使用,并且 V16 中也引入了新的 API 來解決這個問題。 getDerivedStateFromProps 用于替換 componentWillReceiveProps ,該函數(shù)會在初始化和 update 時被調(diào)用
getSnapshotBeforeUpdate 用于替換 componentWillUpdate ,該函數(shù)會在 update 后 DOM 更新前被調(diào)用,用于讀取最新的 DOM 數(shù)據(jù)。 setState setState 在 React 中是經(jīng)常使用的一個 API,但是它存在一些的問題經(jīng)常會導(dǎo)致初學(xué)者出錯,核心原因就是因為這個 API 是異步的。 首先 setState 的調(diào)用并不會馬上引起 state 的改變,并且如果你一次調(diào)用了多個 setState ,那么結(jié)果可能并不如你期待的一樣。 handle() { // 初始化 `count` 為 0 console.log(this.state.count) // -> 0 this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) console.log(this.state.count) // -> 0 } 第一,兩次的打印都為 0,因為 setState 是個異步 API,只有同步代碼運行完畢才會執(zhí)行。setState 異步的原因我認(rèn)為在于,setState 可能會導(dǎo)致 DOM 的重繪,如果域名買賣平臺調(diào)用一次就馬上去進行重繪,那么調(diào)用多次就會造成不必要的性能損失。設(shè)計成異步的話,就可以將多次調(diào)用放入一個隊列中,在恰當(dāng)?shù)臅r候統(tǒng)一進行更新過程。 第二,雖然調(diào)用了三次 setState ,但是 count 的值還是為 1。因為多次調(diào)用會合并為一次,只有當(dāng)更新結(jié)束后 state 才會改變,三次調(diào)用等同于如下代碼 Object.assign( {}, { count: this.state.count + 1 }, { count: this.state.count + 1 }, { count: this.state.count + 1 }, )w 當(dāng)然你也可以通過以下方式來實現(xiàn)調(diào)用三次 setState 使得 count 為 3 handle() { this.setState((prevState)=> ({ count: prevState.count + 1 })) this.setState((prevState)=> ({ count: prevState.count + 1 })) this.setState((prevState)=> ({ count: prevState.count + 1 })) } 如果你想在每次調(diào)用 setState 后獲得正確的 state ,可以通過如下代碼實現(xiàn) handle() { this.setState((prevState)=> ({ count: prevState.count + 1 }), ()=> { console.log(this.state) }) } 性能優(yōu)化 這小節(jié)內(nèi)容集中在組件的性能優(yōu)化上,這一方面的性能優(yōu)化也基本集中在 shouldComponentUpdate 這個鉤子函數(shù)上做文章。 PS:下文中的 state 指代了 state 及 props 在 shouldComponentUpdate 函數(shù)中我們可以通過返回布爾值來決定當(dāng)前組件是否需要更新。這層代碼邏輯可以是簡單地淺比較一下當(dāng)前 state 和之前的 state 是否相同,也可以是判斷某個值更新了才觸發(fā)組件更新。一般來說不推薦完整地對比當(dāng)前 state 和之前的 state 是否相同,因為組件更新觸發(fā)可能會很頻繁,這樣的完整對比性能開銷會有點大,可能會造成得不償失的情況。 當(dāng)然如果真的想完整對比當(dāng)前 state 和之前的 state 是否相同,并且不影響性能也是行得通的,可以通過 immutable 或者 immer 這些庫來生成不可變對象。這類庫對于操作大規(guī)模的數(shù)據(jù)來說會提升不錯的性能,并且一旦改變數(shù)據(jù)就會生成一個新的對象,對比前后 state 是否一致也就方便多了,同時也很推薦閱讀下 immer 的源碼實現(xiàn)。 另外如果只是單純的淺比較一下,可以直接使用 PureComponent,底層就是實現(xiàn)了淺比較 state。 class Test extends React.PureComponent { render() { return ( PureComponent ) } } 這時候你可能會考慮到函數(shù)組件就不能使用這種方式了,如果你使用 16.6.0 之后的版本的話,可以使用 React.memo 來實現(xiàn)相同的功能。 const Test=React.memo(()=> ( PureComponent )) 通過這種方式我們就可以既實現(xiàn)了 shouldComponentUpdate 的淺比較,又能夠使用函數(shù)組件。 通信 其實 React 中的組件通信基本和 Vue 中的一致。同樣也分為以下三種情況: 父子組件通信兄弟組件通信跨多層級組件通信任意組件 父子通信 父組件通過 props 傳遞數(shù)據(jù)給子組件,子組件通過調(diào)用父組件傳來的函數(shù)傳遞數(shù)據(jù)給父組件,這兩種方式是最常用的父子通信實現(xiàn)辦法。 這種父子通信方式也就是典型的單向數(shù)據(jù)流,父組件通過 props 傳遞數(shù)據(jù),子組件不能直接修改 props, 而是必須通過調(diào)用父組件函數(shù)的方式告知父組件修改數(shù)據(jù)。 兄弟組件通信 對于這種情況可以通過共同的父組件來管理狀態(tài)和事件函數(shù)。比如說其中一個兄弟組件調(diào)用父組件傳遞過來的事件函數(shù)修改父組件中的狀態(tài),然后父組件將狀態(tài)傳遞給另一個兄弟組件。 跨多層次組件通信 如果你使用 16.3 以上版本的話,對于這種情況可以使用 Context API。
任意組件 這種方式可以通過 Redux 或者 Event Bus 解決,另外如果你不怕麻煩的話,可以使用這種方式解決上述所有的通信情況 小結(jié) 總的來說這一篇文章的內(nèi)容更多的偏向于 React 的基礎(chǔ),另外 React 的面試題還會經(jīng)常考到 Virtual DOM 中的內(nèi)容,所以這塊內(nèi)容大家也需要好好準(zhǔn)備。 |
|
來自: 新用戶26922hFh > 《待分類》