<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【React】942- 從 setState 聊到 React 性能優(yōu)化

          共 5272字,需瀏覽 11分鐘

           ·

          2021-04-29 09:45

          者:風不識途

          https://segmentfault.com/a/1190000039776687

          setState的同步和異步

          1.為什么使用setState

          • 開發(fā)中我們并不能直接通過修改 state 的值來讓界面發(fā)生更新
            • 因為我們修改了 state 之后, 希望 React 根據(jù)最新的 Stete 來重新渲染界面, 但是這種方式的修改 React 并不知道數(shù)據(jù)發(fā)生了變化
            • React 并沒有實現(xiàn)類似于 Vue2 中的 Object.defineProperty 或者 Vue3 中的Proxy的方式來監(jiān)聽數(shù)據(jù)的變化
            • 我們必須通過 setState 來告知 React 數(shù)據(jù)已經(jīng)發(fā)生了變化
          • 疑惑: 在組件中并沒有實現(xiàn) steState 方法, 為什么可以調(diào)用呢?
            • 原因很簡單: setState方法是從 Component繼承過來的

          2.setState異步更新

          setState是異步更新的

          • 為什么setState設(shè)計為異步呢?
            • setState 設(shè)計為異步其實之前在 GitHub 上也有很多的討論
            • React核心成員(Redux的作者)Dan Abramov也有對應(yīng)的回復, 有興趣的可以看一下
          • 簡單的總結(jié): setState設(shè)計為異步, 可以顯著的提高性能
            • 如果每次調(diào)用 setState 都進行一次更新, 那么意味著 render 函數(shù)會被頻繁的調(diào)用界面重新渲染, 這樣的效率是很低的
            • 最好的方法是獲取到多個更新, 之后進行批量更新
          • 如果同步更新了 state, 但還沒有執(zhí)行 render 函數(shù), 那么stateprops不能保持同步
            • stateprops不能保持一致性, 會在開發(fā)中產(chǎn)生很多的問題

          3.如何獲取異步的結(jié)果

          • 如何獲取 setState 異步更新state后的值?
          • 方式一: setState的回調(diào)
            • setState接收兩個參數(shù): 第二個參數(shù)是回調(diào)函數(shù)(callback), 這個回調(diào)函數(shù)會在state更新后執(zhí)行
          • 方式二: componentDidUpdate生命周期函數(shù)

          3.setState一定是異步的嗎?

          • 其實可以分成兩種情況
          • 在組件生命周期或React合成事件中, setState是異步的
          • setTimeou或原生DOM事件中, setState是同步的
          • 驗證一: 在setTimeout中的更新 —> 同步更新
          • 驗證二: 在原生DOM事件 —> 同步更新

          4.源碼分析

          setState的合并

          1.數(shù)據(jù)的合并

          • 通過setState去修改message,是不會對其他 state 中的數(shù)據(jù)產(chǎn)生影響的
            • 源碼中其實是有對 原對象新對象 進行合并的

          2.多個state的合并

          • 當我們的多次調(diào)用setState, 只會生效最后一次state
          • setState合并時進行累加: 給setState傳遞函數(shù), 使用前一次state中的值

          React 更新機制

          1.React 更新機制

          • 我們在前面已經(jīng)學習React的渲染流程:
          • 那么 React 的更新流程呢?
          • React基本流程

          2.React 更新流程

          • Reactpropsstate 發(fā)生改變時,會調(diào)用 Reactrender 方法,會創(chuàng)建一顆不同的樹

          • React需要基于這兩顆不同的樹之間的差別來判斷如何有效的更新UI

          • 如果一棵樹參考另外一棵樹進行完全比較更新, 那么即使是最先進的算法, 該算法的復雜程度為 O(n 3 ^3 3),其中 n 是樹中元素的數(shù)量

            • 如果在 React 中使用了該算法, 那么展示 1000 個元素所需要執(zhí)行的計算量將在十億的量級范圍
          • 這個開銷太過昂貴了, React的更新性能會變得非常低效

          • 于是,React對這個算法進行了優(yōu)化,將其優(yōu)化成了O(n),如何優(yōu)化的呢?

            • 同層節(jié)點之間相互比較不會跨節(jié)點比較

            • 不同類型的節(jié)點,產(chǎn)生不同的樹結(jié)構(gòu)

            • 開發(fā)中,可以通過key來指定哪些節(jié)點在不同的渲染下保持穩(wěn)定

          情況一: 對比不同類型的元素

          • 節(jié)點為不同的元素React會拆卸原有的樹并且建立起新的樹

            • 當一個元素從 <a> 變成 <img>,從 <Article> 變成 <Comment>,或從 <button> 變成 <div> 都會觸發(fā)一個完整的重建流程

            • 當卸載一棵樹時,對應(yīng)的DOM節(jié)點也會被銷毀,組件實例將執(zhí)行 componentWillUnmount() 方法

            • 當建立一棵新的樹時,對應(yīng)的 DOM 節(jié)點會被創(chuàng)建以及插入到 DOM 中,組件實例將執(zhí)行 componentWillMount() 方法,緊接著 componentDidMount() 方法

          • 比如下面的代碼更改:

            • React 會銷毀 Counter 組件并且重新裝載一個新的組件,而不會對Counter進行復用

          情況二: 對比同一類型的元素

          • 當比對兩個相同類型的 React 元素時,React 會保留 DOM 節(jié)點僅對比更新有改變的屬性
          • 比如下面的代碼更改:
            • 通過比對這兩個元素,React知道只需要修改 DOM 元素上的 className 屬性
          • 比如下面的代碼更改:

            • 當更新 style 屬性時,React 僅更新有所改變的屬性。

            • 通過比對這兩個元素,React 知道只需要修改 DOM 元素上的 color 樣式,無需修改 fontWeight

          • 如果是同類型的組件元素:

            • 組件會保持不變,React會更新該組件的props,并且調(diào)用componentWillReceiveProps()componentWillUpdate() 方法

            • 下一步,調(diào)用 render() 方法,diff 算法將在之前的結(jié)果以及新的結(jié)果中進行遞歸

          情況三: 對子節(jié)點進行遞歸

          • 在默認條件下,當遞歸 DOM 節(jié)點的子元素時,React 會同時遍歷兩個子元素的列表;當產(chǎn)生差異時,生成一個 mutation

            • 我們來看一下在最后插入一條數(shù)據(jù)的情況:??

            • 前面兩個比較是完全相同的,所以不會產(chǎn)生mutation

            • 最后一個比較,產(chǎn)生一個mutation,將其插入到新的DOM樹中即可

          • 但是如果我們是在前面插入一條數(shù)據(jù):

            • React會對每一個子元素產(chǎn)生一個mutation,而不是保持 <li>星際穿越</li><li>盜夢空間</li>的不變
            • 這種低效的比較方式會帶來一定的性能問題

          React 性能優(yōu)化

          1.key的優(yōu)化

          • 我們在前面遍歷列表時,總是會提示一個警告,讓我們加入一個key屬性:
          • 方式一:在最后位置插入數(shù)據(jù)

            • 這種情況,有無key意義并不大
          • 方式二:在前面插入數(shù)據(jù)

            • 這種做法,在沒有 key 的情況下,所有的<li>都需要進行修改
          • 在下面案例: 當子元素 (這里的li元素) 擁有 key

            • React 使用 key 來匹配原有樹上的子元素以及最新樹上的子元素

            • 下面這種場景下, key為 111 和 222 的元素僅僅進行位移,不需要進行任何的修改

            • key333 的元素插入到最前面的位置即可

          key的注意事項:

          • key應(yīng)該是唯一的
          • key不要使用隨機數(shù)(隨機數(shù)在下一次render時,會重新生成一個數(shù)字)
          • 使用index作為key,對性能是沒有優(yōu)化的

          2.render函數(shù)被調(diào)用

          • 我們使用之前的一個嵌套案例:

            • 在App中,我們增加了一個計數(shù)器的代碼
          • 當點擊 +1 時,會重新調(diào)用 Apprender 函數(shù)

            • 而當 App 的 render函數(shù)被調(diào)用時,所有的子組件的 render 函數(shù)都會被重新調(diào)用
          • 那么,我們可以思考一下,在以后的開發(fā)中,我們只要是修改 了App中的數(shù)據(jù),所有的子組件都需要重新render,進行 diff 算法,性能必然是很低的:
            • 事實上,很多的組件沒有必須要重新render
            • 它們調(diào)用 render 應(yīng)該有一個前提,就是依賴的數(shù)據(jù)(state、 props) 發(fā)生改變時再調(diào)用自己的render方法
          • 如何來控制 render 方法是否被調(diào)用呢?
            • 通過shouldComponentUpdate方法即可

          3.shouldComponentUpdate

          React給我們提供了一個生命周期方法 shouldComponentUpdate(很多時候,我們簡稱為SCU),這個方法接受參數(shù),并且需要有返回值;主要作用是:**控制當前類組件對象是否調(diào)用render**方法

          • 該方法有兩個參數(shù):
          • 參數(shù)一: nextProps修改之后, 最新的 porps屬性
          • 參數(shù)二: nextState 修改之后, 最新的 state 屬性
          • 該方法返回值是一個 booolan 類型
          • 返回值為true, 那么就需要調(diào)用 render 方法
          • 返回值為false, 那么不需要調(diào)用 render 方法
          • 比如我們在App中增加一個message屬性:
          • JSX中并沒有依賴這個message, 那么它的改變不應(yīng)該引起重新渲染
          • 但是通過setState修改 state 中的值, 所以最后 render 方法還是被重新調(diào)用了
          // 決定當前類組件對象是否調(diào)用render方法
          // 參數(shù)一: 最新的props
          // 參數(shù)二: 最新的state
          shouldComponentUpdate(nextProps, nextState) {
            // 默認是: return true
            // 不需要在頁面上渲染則不調(diào)用render函數(shù)
            return false
          }

          4.PureComponent

          • 如果所有的類, 我們都需要手動來實現(xiàn) shouldComponentUpdate, 那么會給我們開發(fā)者增加非常多的工作量
            • 我們設(shè)想一下在shouldComponentUpdate中的各種判斷目的是什么?
            • props 或者 state 中數(shù)據(jù)是否發(fā)生了改變, 來決定shouldComponentUpdate返回 truefalse
          • 事實上 React 已經(jīng)考慮到了這一點, 所以 React 已經(jīng)默認幫我們實現(xiàn)好了, 如何實現(xiàn)呢?
            • 將 class 繼承自 PureComponent
            • 內(nèi)部會進行淺層對比最新的 stateporps , 如果組件內(nèi)沒有依賴 porpsstate 將不會調(diào)用render
            • 解決的問題: 比如某些子組件沒有依賴父組件的stateprops, 但卻調(diào)用了render函數(shù)

          5.shallowEqual方法

          這個方法中,調(diào)用 !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState),這個 shallowEqual 就是進行淺層比較:

          6.高階組件memo

          • 函數(shù)式組件如何解決render: 在沒有依賴 stateprops 但卻重新渲染 render 問題

            • 我們需要使用一個高階組件memo

            • 我們將之前的Header、Banner、ProductList都通過 memo 函數(shù)進行一層包裹

            • Footer沒有使用 memo 函數(shù)進行包裹;

            • 最終的效果是,當counter發(fā)生改變時,Header、Banner、ProductList的函數(shù)不會重新執(zhí)行,而 Footer 的函數(shù)會被重新執(zhí)行

          import React, { PureComponent, memo } from 'react'

          // MemoHeader: 沒有依賴props,不會被重新調(diào)用render渲染
          const MemoHeader = memo(function Header({
            console.log('Header被調(diào)用')
            return <h2>我是Header組件</h2>
          })

          1. JavaScript 重溫系列(22篇全)
          2. ECMAScript 重溫系列(10篇全)
          3. JavaScript設(shè)計模式 重溫系列(9篇全)
          4. 正則 / 框架 / 算法等 重溫系列(16篇全)
          5. Webpack4 入門(上)|| Webpack4 入門(下)
          6. MobX 入門(上) ||  MobX 入門(下)
          7. 120+篇原創(chuàng)系列匯總

          回復“加群”與大佬們一起交流學習~

          點擊“閱讀原文”查看 120+ 篇原創(chuàng)文章

          瀏覽 97
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩aaaa | 日逼无码 | 亚洲无码色婷婷 | 日本sm视频 | 69精品人人人 |