React 源碼閱讀-3_032

React 源碼閱讀-3

React.Component

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

React 組件中,代碼重用的主要方式是組合而不是繼承

在 React.Component 的子類中有個必須定義的 render() 函數。本章節介紹其他方法均爲可選。

React.PureComponent

React.PureComponentReact.Component 很相似。兩者的區別在於 React.Component 並未實現 shouldComponentUpdate(),而 React.PureComponent 中以淺層對比propstate 的方式來實現了該函數。

如果賦予 React 組件相同的 propsstate,render() 函數會渲染相同的內容,那麼在某些情況下使用 React.PureComponent 可提高性能。

React.PureComponent 中的 shouldComponentUpdate()僅作對象的淺層比較。如果對象中包含複雜的數據結構,則有可能因爲無法檢查深層的差別,產生錯誤的比對結果。僅在你的 propsstate 較爲簡單時,才使用 React.PureComponent,或者在深層數據結構發生變化時調用 forceUpdate() 來確保組件被正確地更新。你也可以考慮使用 immutable對象加速嵌套數據的比較。

此外,React.PureComponent 中的 shouldComponentUpdate() 將跳過所有子組件樹的 prop 更新。因此,請確保所有子組件也都是“純”的組件。

原理

當組件更新時,如果組件的 propsstate 都沒發生改變, render 方法就不會觸發,省去 Virtual DOM 的生成和比對過程,達到提升性能的目的。具體就是 React 自動幫我們做了一層淺比較:

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

shallowEqual 又做了什麼呢?會比較 Object.keys(state | props) 的長度是否一致,每一個 key 是否兩者都有,並且是否是一個引用,也就是隻比較了第一層的值,確實很淺,所以深層的嵌套數據是對比不出來的。

function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) {
    return true;
  }

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }

  return true;
}

export default shallowEqual;

function is(x: any, y: any) {
  return (
    (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
  );
}

// Object.is()是在ES6中定義的一個新方法,它與‘===’相比,特別針對-0、+0、NaN做了處理。Object.is(-0, +0)會返回false,而Object.is(NaN, NaN)會返回true。這與===的判斷恰好相反,也更加符合我們的預期。
https://www.imweb.io/topic/59...

使用指南

易變數據不能使用一個引用

class App extends PureComponent {
  state = {
    items: [1, 2, 3]
  }
  handleClick = () => {
    const { items } = this.state;
    items.pop();
    this.setState({ items });
  }
  render() {
    return (<div>
      <ul>
        {this.state.items.map(i => <li key={i}>{i}</li>)}
      </ul>
      <button onClick={this.handleClick}>delete</button>
    </div>)
  }
}

會發現,無論怎麼點 delete 按鈕, li都不會變少,因爲 items 用的是一個引用, shallowEqual 的結果爲 true 。改正:

handleClick = () => {
  const { items } = this.state;
  items.pop();
  this.setState({ items: [].concat(items) });
}

不變數據使用一個引用

子組件數據

如果是基本類型, 是能夠更新的.
引用類型,則不會更新.

handleClick = () => {
  const { items } = this.state;
  items.splice(items.length - 1, 1);
  this.setState({ items });
}

子組件裏還是re-render了。這樣就需要我們保證不變的子組件數據的引用不能改變。這個時候可以使用immutable-js函數庫。

函數屬性

// 1
<MyInput onChange={e => this.props.update(e.target.value)} />
// 2
update(e) {
  this.props.update(e.target.value)
}
render() {
  return <MyInput onChange={this.update.bind(this)} />
}

由於每次 render 操作 MyInput 組件的 onChange 屬性都會返回一個新的函數,由於引用不一樣,所以父組件的 render 也會導致 MyInput 組件的 render ,即使沒有任何改動,所以需要儘量避免這樣的寫法,最好這樣寫:

// 1,2
update = (e) => {
  this.props.update(e.target.value)
}
render() {
  return <MyInput onChange={this.update} />
}

空對象、空數組或固定對象

有時候後臺返回的數據中,數組長度爲0或者對象沒有屬性會直接給一個 null ,這時候我們需要做一些容錯:

class App extends PureComponent {
  state = {
    items: [{ name: 'test1' }, null, { name: 'test3'  }]
  }
  store = (id, value) => {
    const { items } = this.state;
    items[id]  = assign({}, items[id], { name: value });
    this.setState({ items: [].concat(items) });
  }
  render() {
    return (<div>
      <ul>
        {this.state.items.map((i, k) =>
          <Item style={{ color: 'red' }} store={this.store} key={k} id={k} data={i || {}} />)
        }
      </ul>
    </div>)
  }
}

PureComponent 真正起作用的,只是在一些純展示組件上,複雜組件用了也沒關係,反正 shallowEqual 那一關就過不了,不過記得 propsstate 不能使用同一個引用哦。

組件的生命的週期

生命週期地址

img

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章