<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                React 生命周期很多人都了解,但通常我們所了解的都是**單個組件**的生命周期,但針對**Hooks 組件、多個關聯組件**(父子組件和兄弟組件) 的生命周期又是怎么樣的喃?你有思考和了解過嗎,接下來我們將完整的了解 React 生命周期。 關于**組件**,我們這里指的是`React.Component`以及`React.PureComponent`,但是否包括 Hooks 組件喃? ## 一、Hooks 組件 **函數組件**的本質是函數,沒有 state 的概念的,因此**不存在生命周期**一說,僅僅是一個**render 函數**而已。 但是引入**Hooks**之后就變得不同了,它能讓組件在不使用 class 的情況下使用 state 以及其他的 React特性,相比與 class 的生命周期概念來說,它更接近于實現狀態同步,而不是響應生命周期事件。但我們可以利用`useState`、`useEffect()`和`useLayoutEffect()`來模擬實現生命周期。 即:**Hooks 組件更接近于實現狀態同步,而不是響應生命周期事件**。 下面,是具體的 生命周期 與 Hooks 的**對應關系**: * `constructor`:函數組件不需要構造函數,我們可以通過調用**`useState`來初始化 state**。如果計算的代價比較昂貴,也可以傳一個函數給`useState`。 ~~~js const [num, UpdateNum] = useState(0) ~~~ * `getDerivedStateFromProps`:一般情況下,我們不需要使用它,我們可以在**渲染過程中更新 state**,以達到實現`getDerivedStateFromProps`的目的。 ~~~js function ScrollView({row}) { let [isScrollingDown, setIsScrollingDown] = useState(false); let [prevRow, setPrevRow] = useState(null); if (row !== prevRow) { // Row 自上次渲染以來發生過改變。更新 isScrollingDown。 setIsScrollingDown(prevRow !== null && row > prevRow); setPrevRow(row); } return `Scrolling down: ${isScrollingDown}`; } ~~~ React 會立即退出第一次渲染并用更新后的 state 重新運行組件以避免耗費太多性能。 * `shouldComponentUpdate`:可以用?**`React.memo`**?包裹一個組件來對它的`props`進行淺比較 ~~~js const Button = React.memo((props) => { // 具體的組件 }); ~~~ 注意:**`React.memo`?等效于?`PureComponent`**,它只淺比較 props。這里也可以使用`useMemo`優化每一個節點。 * `render`:這是函數組件體本身。 * `componentDidMount`,`componentDidUpdate`:?`useLayoutEffect`?與它們兩的調用階段是一樣的。但是,我們推薦你**一開始先用?useEffect**,只有當它出問題的時候再嘗試使用?`useLayoutEffect`。`useEffect`可以表達所有這些的組合。 ~~~js // componentDidMount useEffect(()=>{ // 需要在 componentDidMount 執行的內容 }, []) useEffect(() => { // 在 componentDidMount,以及 count 更改時 componentDidUpdate 執行的內容 document.title = `You clicked ${count} times`; return () => { // 需要在 count 更改時 componentDidUpdate(先于 document.title = ... 執行,遵守先清理后更新) // 以及 componentWillUnmount 執行的內容 } // 當函數中 Cleanup 函數會按照在代碼中定義的順序先后執行,與函數本身的特性無關 }, [count]); // 僅在 count 更改時更新 ~~~ **請記得 React 會等待瀏覽器完成畫面渲染之后才會延遲調用?`useEffect`,因此會使得額外操作很方便** * `componentWillUnmount`:相當于`useEffect`里面返回的`cleanup`函數 ~~~js // componentDidMount/componentWillUnmount useEffect(()=>{ // 需要在 componentDidMount 執行的內容 return function cleanup() { // 需要在 componentWillUnmount 執行的內容 } }, []) ~~~ * `componentDidCatch`and`getDerivedStateFromError`:目前**還沒有**這些方法的 Hook 等價寫法,但很快會加上。 為方便記憶,大致匯總成表格如下。 | class 組件 | Hooks 組件 | | --- | --- | | constructor | useState | | getDerivedStateFromProps | useState 里面 update 函數 | | shouldComponentUpdate | useMemo | | render | 函數本身 | | componentDidMount | useEffect | | componentDidUpdate | useEffect | | componentWillUnmount | useEffect 里面返回的函數 | | componentDidCatch | 無 | | getDerivedStateFromError | 無 | ## 二、單個組件的生命周期 1. 生命周期 V16.3 之前 我們可以將生命周期分為三個階段: * 掛載階段 * 組件更新階段 * 卸載階段 分開來講: 1. 掛載階段 * `constructor`:避免將 props 的值復制給 state * `componentWillMount` * `render`:react 最重要的步驟,創建虛擬 dom,進行 diff 算法,更新 dom 樹都在此進行 * `componentDidMount` 2. 組件更新階段 * `componentWillReceiveProps` * `shouldComponentUpdate` * `componentWillUpdate` * `render` * `componentDidUpdate` 3. 卸載階段 * `componentWillUnMount` ![](https://img.kancloud.cn/00/c7/00c7111a5d284f6346316fac3358c54a_2000x924.png) 這種生命周期會存在一個問題,那就是當更新復雜組件的最上層組件時,調用棧會很長,如果在進行復雜的操作時,就可能長時間阻塞主線程,帶來不好的用戶體驗,**Fiber**就是為了解決該問題而生。 V16.3 之后 **Fiber 本質上是一個虛擬的堆棧幀,新的調度器會按照優先級自由調度這些幀,從而將之前的同步渲染改成了異步渲染,在不影響體驗的情況下去分段計算更新。** 對于異步渲染,分為兩階段: * `reconciliation`: * `componentWillMount` * `componentWillReceiveProps` * `shouldConmponentUpdate` * `componentWillUpdate` * `commit` * `componentDidMount` * `componentDidUpdate` 其中,`reconciliation`階段是可以被打斷的,所以`reconcilation`階段執行的函數就會出現多次調用的情況,顯然,這是不合理的。 所以 V16.3 引入了新的 API 來解決這個問題: 1. `static getDerivedStateFromProps`:?該函數在**掛載階段和組件更新階段**都會執行,即**每次獲取新的`props`或`state`之后都會被執行**,**在掛載階段用來代替`componentWillMount`**;在組件更新階段配合`componentDidUpdate`,可以覆蓋`componentWillReceiveProps`的所有用法。 同時它是一個靜態函數,所以函數體內不能訪問`this`,會根據`nextProps`和`prevState`計算出預期的狀態改變,返回結果會被送給`setState`**,**返回`null`則說明不需要更新`state`,并且這個返回是**必須的**。 2. `getSnapshotBeforeUpdate`: 該函數會在?**`render`?之后, DOM 更新前**被調用,用于讀取最新的 DOM 數據。 返回一個值,**作為`componentDidUpdate`的第三個參數**;配合`componentDidUpdate`, 可以覆蓋`componentWillUpdate`的所有用法。 注意:V16.3 中只用在組件掛載或組件`props`更新過程才會調用,即如果是因為自身 setState 引發或者forceUpdate 引發,而不是由父組件引發的話,那么`static getDerivedStateFromProps`也不會被調用,在 V16.4 中更正為都調用。 即更新后的生命周期為: 1. 掛載階段 * `constructor` * `static getDerivedStateFromProps` * `render` * `componentDidMount` 2. 更新階段 * `static getDerivedStateFromProps` * `shouldComponentUpdate` * `render` * `getSnapshotBeforeUpdate` * `componentDidUpdate` 3. 卸載階段 * `componentWillUnmount` ![](https://img.kancloud.cn/2d/9e/2d9e305c313d9975f63c3dc58af376b3_2228x1470.png) 2. 生命周期,誤區 **誤解一:**`getDerivedStateFromProps`?和?`componentWillReceiveProps`?只會在`props`**改變**時才會調用 實際上,**只要父級重新渲染,`getDerivedStateFromProps`?和?`componentWillReceiveProps`?都會重新調用,不管`props`有沒有變化**。所以,在這兩個方法內直接將 props 賦值到 state 是不安全的。 ~~~js // 子組件 class PhoneInput extends Component { state = { phone: this.props.phone }; handleChange = e => { this.setState({ phone: e.target.value }); }; render() { const { phone } = this.state; return <input onChange={this.handleChange} value={phone} />; } componentWillReceiveProps(nextProps) { // 不要這樣做。 // 這會覆蓋掉之前所有的組件內 state 更新! this.setState({ phone: nextProps.phone }); } } // 父組件 class App extends Component { constructor() { super(); this.state = { count: 0 }; } componentDidMount() { // 使用了 setInterval, // 每秒鐘都會更新一下 state.count // 這將導致 App 每秒鐘重新渲染一次 this.interval = setInterval( () => this.setState(prevState => ({ count: prevState.count + 1 })), 1000 ); } componentWillUnmount() { clearInterval(this.interval); } render() { return ( <> <p> Start editing to see some magic happen :) </p> <PhoneInput phone='call me!' /> <p> This component will re-render every second. Each time it renders, the text you type will be reset. This illustrates a derived state anti-pattern. </p> </> ); } } ~~~ [實例可點擊這里查看](https://stackblitz.com/edit/react-yammav) 當然,我們可以在 父組件App 中`shouldComponentUpdate`比較?props 的 email 是不是修改再決定要不要重新渲染,但是如果子組件接受多個 props(較為復雜),就很難處理,而且`shouldComponentUpdate`主要是用來性能提升的,不推薦開發者操作`shouldComponetUpdate`(可以使用`React.PureComponet`)。 我們也可以使用**在 props 變化后修改 state**。 ~~~js class PhoneInput extends Component { state = { phone: this.props.phone }; componentWillReceiveProps(nextProps) { // 只要 props.phone 改變,就改變 state if (nextProps.phone !== this.props.phone) { this.setState({ phone: nextProps.phone }); } } // ... } ~~~ 但這種也會導致一個問題,當 props 較為復雜時,props 與 state 的關系不好控制,可能導致問題 解決方案一:**完全可控的組件** ~~~js function PhoneInput(props) { return <input onChange={props.onChange} value={props.phone} />; } ~~~ **完全由 props 控制,不派生 state** 解決方案二:**有 key 的非可控組件** ~~~js class PhoneInput extends Component { state = { phone: this.props.defaultPhone }; handleChange = event => { this.setState({ phone: event.target.value }); }; render() { return <input onChange={this.handleChange} value={this.state.phone} />; } } <PhoneInput defaultPhone={this.props.user.phone} key={this.props.user.id} /> ~~~ 當?`key`?變化時, React 會**創建一個新的而不是更新一個既有的組件** **誤解二**:將 props 的值直接復制給 state **應避免將 props 的值復制給 state** ~~~js constructor(props) { super(props); // 千萬不要這樣做 // 直接用 props,保證單一數據源 this.state = { phone: props.phone }; } ~~~ ## 三、多個組件的執行順序 1. 父子組件 * **掛載階段** 分**兩個**階段: * 第**一**階段,由父組件開始執行到自身的`render`,解析其下有哪些子組件需要渲染,并對其中**同步的子組件**進行創建,按**遞歸順序**挨個執行各個子組件至`render`,生成到父子組件對應的 Virtual DOM 樹,并 commit 到 DOM。 * 第**二**階段,此時 DOM 節點已經生成完畢,組件掛載完成,開始后續流程。先依次觸發同步子組件各自的`componentDidMount`,最后觸發父組件的。 **注意**:如果父組件中包含異步子組件,則會在父組件掛載完成后被創建。 所以執行順序是: 父組件 getDerivedStateFromProps —> 同步子組件 getDerivedStateFromProps —> 同步子組件 componentDidMount —> 父組件 componentDidMount —> 異步子組件 getDerivedStateFromProps —> 異步子組件 componentDidMount * **更新階段** **React 的設計遵循單向數據流模型**,也就是說,數據均是由父組件流向子組件。 * 第**一**階段,由父組件開始,執行 1. `static getDerivedStateFromProps` 2. `shouldComponentUpdate` 更新到自身的`render`,解析其下有哪些子組件需要渲染,并對**子組件**進行創建,按**遞歸順序**挨個執行各個子組件至`render`,生成到父子組件對應的 Virtual DOM 樹,并與已有的 Virtual DOM 樹 比較,計算出**Virtual DOM 真正變化的部分**,并只針對該部分進行的原生DOM操作。 * 第**二**階段,此時 DOM 節點已經生成完畢,組件掛載完成,開始后續流程。先依次觸發同步子組件以下函數,最后觸發父組件的。 1. `getSnapshotBeforeUpdate()` 2. `componentDidUpdate()` React 會按照上面的順序依次執行這些函數,每個函數都是各個子組件的先執行,然后才是父組件的執行。 所以執行順序是: 父組件 getDerivedStateFromProps —> 父組件 shouldComponentUpdate —> 子組件 getDerivedStateFromProps —> 子組件 shouldComponentUpdate —> 子組件 getSnapshotBeforeUpdate —> 父組件 getSnapshotBeforeUpdate —> 子組件 componentDidUpdate —> 父組件 componentDidUpdate * **卸載階段** `componentWillUnmount()`,順序為**父組件的先執行,子組件按照在 JSX 中定義的順序依次執行各自的方法**。 **注意**:如果卸載舊組件的同時伴隨有新組件的創建,新組件會先被創建并執行完`render`,然后卸載不需要的舊組件,最后新組件執行掛載完成的回調。 2. 兄弟組件 * **掛載階段** 若是同步路由,它們的創建順序和其在共同父組件中定義的先后順序是**一致**的。 若是異步路由,它們的創建順序和 js 加載完成的順序一致。 * **更新階段、卸載階段** 兄弟節點之間的通信主要是經過父組件(Redux 和 Context 也是通過改變父組件傳遞下來的?`props`?實現的),**滿足React 的設計遵循單向數據流模型**,**因此任何兩個組件之間的通信,本質上都可以歸結為父子組件更新的情況**。 所以,兄弟組件更新、卸載階段,請參考**父子組件**。 走在最后:走心推薦一個在線編輯工具:[StackBlitz](https://stackblitz.com/),可以在線編輯 Angular、React、TypeScript、RxJS、Ionic、Svelte項目 ## 摘自 [ 你真的了解 React 生命周期嗎?](https://github.com/sisterAn/blog/blob/master/articles/React%E7%B3%BB%E5%88%97/Hooks%20%E4%B8%8E%20React%20%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E7%9A%84%E5%85%B3%E7%B3%BB.md)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看