本文旨在討論 React 中的 render(渲染) 一詞的含義?react
若是你已經接觸了 React 一段時間,你常常會看到 render(渲染),rerender(重渲染),等各類與渲染相關的字眼,當我做爲一個菜鳥時,我時常爲這些字眼發愁,儘管我知道這些詞語的本意,可是卻不明白它們在 React 中表明什麼。如今,通過必定量的學習,我以爲本身終於能夠看清一部分,藉此分享出來,以期幫助別人,同時也是藉此梳理下本身掌握的知識。數組
爲了理解 render 在 React 中表明什麼,咱們須要稍微深刻下 React 的工做原理,也許你已經知道虛擬DOM,Diff,等各類新奇的詞語,不過仍是讓咱們按順序梳理如下。dom
React 進行渲染的 時機 有兩種(其實也能夠算一種,其中一種只是另外一種的變體):函數
掛載,React 調用類組件 DidComponentMount 生命週期方法時,或者函數組件的useEffect中回調函數第一次執行的時候,能夠認爲已經發生了掛載。學習
更新,React 調用類組件的 DidComponentUpdate 的時候,或者函數組件被從新執行時,均可以認爲是發生了重渲染。spa
咱們首先來看組件更新的狀況,而後再回頭看掛載的狀況,你會發現,後者和前者的差異其實很小,這也是爲何 React 在函數組件裏提供的 useEffect hook 再也不區分掛載和更新。code
讓咱們使用經典的計數器例子生命週期
// 警告:爲了代碼的精簡的,我省略了 useCallback 包裹回調函數的行爲
function Counter() {
const [count, setCount] = useState();
return (
<div>
<span className="count">{count}</span>
<button onClick={() => setCount(prev => prev + 1)}>INC<button>
</div>
);
}
複製代碼
在 Counter 掛載後,當你點擊了 INC 按鈕時,就會觸發一次 React 的組件的更新 或者說是重渲染。jsx
這裏爲了講解的方便,我自做主張的將 React 的一個完整的渲染週期的分爲三個階段,注意不要聯想到生命週期,這裏沒有任何關係。回調函數
三個階段是:
渲染函數階段 或 render函數階段,這個階段會執行render函數,生成虛擬DOM。
tree diff 階段,這個階段 React 會對比新舊的的虛擬DOM,看看是否有變化發生。
commit 階段,若是確實發生了變化,纔會進入這個階段,這個階段會將虛擬DOM中發生的變化同步到真實DOM上。
通常來講,前兩個階段能夠視爲一個階段,由於它們都發生在虛擬DOM層面,而且一旦觸發渲染函數階段,則必定會觸發 tree diff 階段,可是 commit 階段只在虛擬DOM發生變化的時才能夠被觸發。
在瞭解組件更新的的流程後,咱們對比着更新的流程,回過頭來看組件掛載的狀況:
答案: 確定有!必需要執行 render 函數,生成虛擬DOM。
答案:這裏視場景有不一樣的答案,不嚴謹的說,ReactDOM.render() 是沒有這個階段的,由於首次執行還不存在能夠進行對比的舊虛擬DOM,會直接跳到 commit 階段。而其餘掛載的狀況,被掛載的組件自身也是不會進行 tree diff 的。
這裏爲了不誤解多說一點,父組件更新致使某個子組件被掛載或卸載,子組件掛載時自身是不會進行 tree diff 的,視狀況會直接跳到 commit 階段或就此結束。可是父組件進行 tree diff 時會用到這個子組件。你能夠認爲 null 指是一個特殊的虛擬DOM,React 將它做爲佔位符使用,在進行 tree diff 的時候也有相關的做用。
答案:這點毋庸置疑。
如今,讓咱們經過肯定 「這個組件發生了渲染/重渲染/無用的重渲染」 是什麼意思來解答本小節的問題。
對於 ...發生了渲染/重渲染 來講都是指階段123,也就是整個渲染階段。生成變化後的虛擬DOM,進行 tree diff,將變化 commit 到真實 DOM 上。若是不是在指明討論虛擬 DOM 的話,將渲染一詞侷限一、2階段時沒有意義的。
而對於 發生了無效的重渲染 來講,是指階段一、2。通常發生在父組件發生了更新,子組件的props卻沒有變化,因此子組件生成的虛擬dom沒有發生改變,在 tree diff 階段後直接斷掉了,沒有進入 commit 階段。
之後再碰到與 React 渲染相關的問題,能夠直接用上文講解的三個階段往上套,經過理解上下文,應該能夠很容易理解做者想要的強調有關渲染的什麼方面。