react 知識整理

生命週期

生命週期

其中標紅的( componentWillMount、 componentWillReceiveProps、 componentWillUpdate )是 react 16.3 版本要移除的生命週期,移除緣由:react 打算在17版本中,添加 async rendering , react 將一個更新過程分爲 render 先後兩個階段,render 前是能夠被打斷(好比有緊急任務),當生命週期被打斷後,再次執行,並不會從斷點繼續執行,是從新執行的,因此這些生命週期就可能會運行屢次。html

同時爲了彌補失去這三個生命,react 新增了兩個生命週期: static getDerivedStateFromProps、 getSnapshotBeforeUpdatereact

static getDerivedStateFromProps

getDerivedStateFromProps 會在調用 render 方法以前調用,而且在初始掛載及後續更新時都會被調用。它應返回一個對象來更新 state,若是返回 null 則不更新任何內容。算法

此方法適用於罕見的用例,即 state 的值在任什麼時候候都取決於 props。編程

static getDerivedStateFromProps(nextProps, prevState) {
  //根據nextProps和prevState計算出預期的狀態改變,返回結果會被送給setState
  //這是一個static,簡單說應該是一個純函數
}
複製代碼

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate() 在最近一次渲染輸出(提交到 DOM 節點)以前調用。它使得組件能在發生更改以前從 DOM 中捕獲一些信息(例如,滾動位置)。今生命週期的任何返回值將做爲參數傳遞給 componentDidUpdate()。api

getSnapshotBeforeUpdate 返回的值會作爲第三個參數傳遞給 componentDidUpdate。數組

getSnapshotBeforeUpdate(prevProps, prevState) {
  ...
  return snapshot;
}

componentDidUpdate(prevProps, prevState, snapshot) {
  
}
複製代碼

性能優化

shouldComponentUpdate

這是一個組件的子樹。每一個節點中,SCU 表明 shouldComponentUpdate 返回的值,而 vDOMEq 表明返回的 React 元素是否相同。最後,圓圈的顏色表明了該組件是否須要更新。性能優化

shouldComponentUpdate

react 父組件觸發更新時,它的全部子組件都會觸發更新(即便 props 和 state )並無改變,這樣時候咱們在子組件中添加 shouldComponentUpdate 生命週期,判斷狀態是否變動,若是沒有變動返回 false , 這個子組件就不會從新 render。markdown

React.PureComponent

React.PureComponent 是 React 自動幫咱們在 shouldComponentUpdate 作了一層淺比較。app

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps)
  || !shallowEqual(inst.state, nextState);
}
複製代碼

React.memo

React.memo 是一個高階組件,相似於 React.PureComponent,不一樣於 React.memo 是 function 組件,React.PureComponent 是 class 組件。dom

const MyComponent = React.memo(function MyComponent(props) {
  /* 使用 props 渲染 */
});
複製代碼

長列表優化

  • 虛擬列表:用數組保存全部列表元素的位置,只渲染可視區內的列表元素,當可視區滾動時,根據滾動的 offset 大小以及全部列表元素的位置,計算在可視區應該渲染哪些元素。經常使用庫( react-window 、 react-virtualized )
  • 事件代理:簡單的說就是將同類行的子元素事件,綁定在一個父元素上,從而達到減小事件的註冊。
  • 懶加載:經常使用於長列表圖片加載,組件先不渲染,當監聽到組件能夠時,在去渲染組件。經常使用庫( react-lazyload )

key

key 幫助 React 識別哪些元素改變了,好比被添加或刪除。當子元素擁有 key 時,React 使用 key 來匹配原有樹上的子元素以及最新樹上的子元素。正確的使用 key 可使 react 的更新開銷變小。

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>
複製代碼

代碼複用

Mixin

概念:將公用方法包裝成 Mixin 方法,而後注入各個組件,從而實現代碼複用。( 已經不推薦 )

缺點:

  • 命名覆蓋:好比你在 A Mixin 中定義的 get 方法,在 B Mixin 中也定義了 get 方法。
  • 複雜度高後,容易混亂:好比 Mixin 中能夠調用 setState 的,當多個 Mixin 中都調用了 setSate , state 的更新來源會變得混淆不清。
  • ES6 class 語法不支持 Mixin。

HOC(高階組件)

概念:能夠理解爲組件工廠,傳入原始組件,添加功能,返回新的組件。

缺點:

  • 命名覆蓋,難以溯源:當存在多個 HOC 時,僅僅經過新組件,並不能知道 props 是來源於哪一個 HOC,同時若是兩個工廠,傳入了相同名稱的props,就會產生覆蓋。
  • 靜態構建:工廠返回的新組件,不會當即執行,即 HOC 工廠函數裏定義的生命週期函數只有新組建渲染時纔會執行。

Render Props

概念:Render Props 就是一個 render 函數做爲 props 傳遞給了父組件,使得父組件知道如何渲染子組件。

優勢:

  • 解決了命名衝突,難以溯源的問題,能夠經過 render props 的參數直接看到 props 來源於哪一個組件。
  • 動態構建,能夠更好的利用組件內的生命週期。

缺點:

  • 沒法利用 shouldComponentUpdate 來實現渲染性能的優化。

React hooks

概念:hooks 遵循函數式編程的理念,主旨是在函數組件中引入類組件中的狀態和生命週期,而且這些狀態和生命週期函數也能夠被抽離,實現複用的同時,減小函數組件的複雜性和易用性。

hooks api:

  • 基礎:useState、 useEffect、 useContext
  • 額外:useReducer、 useCallback、 useMemo、 useRef、 useImperativeHandle、 useLayoutEffect、 useDebugValue

一個簡單的 custom hooks:

import { useEffect } from 'react';

function useTitle(title){
  useEffect(()=>{
    document.title = title;
  }, [title]);
}
複製代碼

虛擬 Dom diff 算法

虛擬 Dom:react 將 Dom 抽象成一個對象樹,經過對比新舊兩個樹的區別(diff 算法),而後將更新部分渲染出來。

diff 算法基於兩個策略:

  • 兩個相同組件產生相似的 DOM 結構,不一樣的組件產生不一樣的 DOM 結構。
  • 對於同一層次的一組子節點,它們能夠經過惟一的 id 進行區分。

逐層進行節點比較

在 react 中,樹的比對很是簡單,react 只會對兩棵樹進行逐層比較,即比較處於同一層級的節點。

逐層比較

節點比較

節點比較分紅兩種狀況:(1)節點類型不一樣,(2)節點類型相同,屬性不一樣。

狀況(1):

節點不一樣

// DOM Diff 操做
C.destroy();
D = new D();
A.append(D);
複製代碼

狀況(2):

//變動錢
<div style={{fontSize: '16px'}} ></div>
//變動後
<div style={{color: 'red'}} ><div/> //執行操做 => removeStyle fontSize => addStyle color 'red' 複製代碼

列表節點的比較(key)

當列表中插入 F 節點,以下圖:

列表節點的比較

這個時候就會有兩種狀況:(1)節點沒有表示惟一 key ,(2)節點表示了惟一 key

狀況(1)結果以下圖:

列表節點的比較,沒有惟一 key

//執行過程
C unmount
D unmount
E unmount
F mount
C mount
D mount
E mount
複製代碼

狀況(2)結果以下圖:

列表節點的比較,有惟一 key

//執行過程
F mount
複製代碼

React 16.x 部分新特性

Error Boundaries

若是在組件的渲染或生命週期方法中引起錯誤,整個組件結構就會從根節點中卸載,而不影響其餘組件的渲染,能夠利用 error boundaries 進行錯誤的優化處理。

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}
複製代碼

createPortal

react 支持將組建掛在到其餘 dom 節點,事件仍是按組件原有位置冒泡。

render() {
  return ReactDOM.createPortal(
    this.props.children,
    domNode,
  );
}
複製代碼

Fiber

Fiber 是對 react 核心算法 reconciliation 的更新實現,將本來的同步更新分紅兩個階段。階段1(Reconciliation Phase)的任務是片斷化執行的,每一個片斷執行完成以後,就會把控制權從新交給 react 調度模塊,若是有更高優先級的任務就去處理,而低優先級更新任務所作的工做則會徹底做廢,而後等待機會重頭再來,若是沒有就執行下一個片斷。階段2(Commit Phase)是同步執行的,reactDom 會根據階段1計算出來的 effect-list 來更新 DOM 。

階段1涉及的生命週期(可能會執行屢次):componentWillMount、 componentWillReceiveProps、 shouldComponentUpdate、 componentWillUpdate

階段2涉及的生命週期:componentDidMount、 componentDidUpdate、 componentWillUnmount

createContext

全新的 Context API 能夠很容易實現祖先節點和子節點通訊,而且沒有反作用。

  • React.createContext 是一個函數,它接收初始值並返回帶有 Provider 和 Consumer 組件的對象
  • Provider 發佈方
  • Consumer 訂閱方
const ThemeContext = React.createContext('light');

class ThemeProvider extends React.Component {
  state = {theme: 'light'};

  render() {
    return (
      <ThemeContext.Provider value={this.state.theme}> {this.props.children} </ThemeContext.Provider>
    );
  }
}

class ThemedButton extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer> {theme => <Button theme={theme} />} </ThemeContext.Consumer>
    );
  }
}
複製代碼

生命週期函數的更新(見上文)

React.memo(見上文)

lazy / Suspense

React.lazy() 提供了動態 import 組件的能力,實現代碼分割。

Suspense 做用是在等待組件時 suspend(暫停)渲染,並顯示加載標識。

import React, {lazy, Suspense} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}> <OtherComponent /> </Suspense>
  );
}
複製代碼

React hooks(見上文)

相關文章
相關標籤/搜索