React的父子組件生命周期


父子組件生命周期:

  “生命周期”細想之下有點浪漫主義色彩,不知道是不是從lifecycle英譯過來的。作為一個前端從業者,如果讓我來取,可能會取成“渲染周期”之類的,畢竟是和瀏覽器打交道的職業,瀏覽器的layout使dom樹具有骨架,paint則讓整個頁面光亮起來。

  React 的一切都是組件,通過 React.createElement 方法來創建嵌套層級,說白了在內存中構建對象樹,據此渲染到瀏覽器中成為dom樹,這個時候一個節點是什么時候真正渲染到頁面中就變得重要起來,因為只有這個時候你才能真正和瀏覽器環境內的對象和方法交互,同樣離開的時候也需要清理監聽器等防止干擾后續邏輯,因此鈎子函數,也可以說是生命周期函數就有了存在的意義。

  先上代碼:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <div id="hollow"></div>
    <script type="text/babel">
      const { Component, Fragment } = React;
      class F extends Component {
        state = { x: 'state before' };
        static getDerivedStateFromProps(getProps, getState) {
          console.log('F get props', { getProps, getState });
        }
        componentDidMount() {
          console.log('F did mount');
          setTimeout(() => {
            console.log('%c %s', 'color:blue', 'start to update state');
            this.setState({ x: 'state after' });
          }, 2000);
        }
        shouldComponentUpdate(nextProps, nextState) {
          console.log('F should update', { nextProps, nextState });
          return true;
        }
        componentDidUpdate(prevProps, prevState) {
          console.log('F did update', { prevProps, prevState });
        }
        componentWillUnmount() {
          console.log('F will unmount', Date.now());
        }
        render() {
          return (
            <div>
              {this.props.x}
              {this.state.x}
            </div>
          );
        }
      }
      class App extends Component {
        state = { x: 'props before ' };
        componentDidMount() {
          console.log('App did mount');
          setTimeout(() => {
            console.log('%c %s', 'color:red', 'start to update props');
            this.setState({ x: 'props after ' });
          }, 2000);
        }
        componentWillUnmount() {
          console.log('App will unmount', Date.now());
        }
        render() {
          return <F {...this.state} />;
        }
      }
      setTimeout(() => {
        ReactDOM.render('unmount Component App at ' + Date.now(), app);
      }, 6000);
      ReactDOM.render(<App />, app);
    </script>
  </body>
</html>

---CodePen在線演示---

Props 和 State 相關

父組件 App 將自身的State傳入了子組件 F 內,忽略掛載和卸載,列舉生命周期函數:

1,getDerivedStateFromProps --

  通過瀏覽器打印結果可以看到,不管是組件初始化,還是更新或繼承新的 state 或者 props ,最先觸發的都是靜態鈎子函數 getDerivedStateFromProps ;

2,shouldComponentUpdate --

  在 setState 異步賦值了 state 的下一個狀態后,整個子組件 F 開始收集和對比新舊狀態,將新的狀態輸入到生命周期函數 shouldComponentUpdate 中,寫明 return 的值是 truthy 還是 falsy 可以選擇中斷更新或者繼續更新;

3,componentDidUpdate --

  在 shouldComponentUpdate 順利進入下一步后,將執行 render 方法,更新虛擬dom樹,瀏覽器完成渲染,在此之后舊的狀態將作為參數傳給 componentDidUpdate ,可以對比新狀態以及是否保留舊狀態;

掛載 和 卸載

componentDidMount 和 componentWillUnmount --

  觀察父子組件的掛載生命周期函數,可以發現掛載時,子組件的掛載鈎子先被觸發;卸載時,子組件的卸載鈎子后被觸發;

  對於掛載鈎子,一般來說,應該將子組件從上至下依次掛載到一個 fragment 上,再整體掛載到dom樹中,因為頻繁操作dom樹不僅影響性能甚至可能影響用戶體驗。

  但是實際情況卻並如此,我們經常在掛載函數上注冊監聽器,說明此時是可以與頁面交互的,也就是說其實所有掛載鈎子都是在父組件實際掛載到dom樹上才觸發的,不過是在父組件掛載后依次觸發子組件的 componentDidmount ,最后再觸發自身的掛載鈎子,說白了,componentDidMount 其實是異步鈎子。

  相反,卸載的時候父節點先被移除,再從上至下依次觸發子組件的卸載鈎子;

  但是我們也經常在卸載鈎子上卸載監聽器,這說明 componentWillUnmount 其實在父組件從dom樹上卸載前觸發的,先觸發自身的卸載鈎子,但此時並未從dom樹上剝離,然后依次嘗試觸發所有子組件的卸載鈎子,最后,父組件從dom樹上完成實際卸載。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM