【React性能優化】 React組件優化

簡介:react

1.組件內: 傳遞參數的時候要少傳,且每次傳遞的儘可能是一份數據,不要每次這個數據都會被從新建立
2.組件間:shouldComponent、pureComponent、immutable

單組件 - 屬性傳遞優化

  1. 函數綁定優化 - 綁定函數的幾種方式
handleClick(event){
    // 你的事件處理邏輯
  }

1. onclick={::this.handlerClick} 不建議,es7中的寫法,等同於3
   onClick={this.handleClick.bind(this)}
// 箭頭函數中的上下文是所在全局的上下文,不決定於誰調用他
2. onclick={() => this.handlerClick()}
   onClick={this.handleClick}

   handleClick = () => {
    console.log(this); 
  }
// 因此咱們須要在class的construct中將函數的執行做用域設爲當前組件
3. constructor(props, context) {
       super(props, context)
       this.handlerClick= this.handlerClick.bind(this)
   }
   onclick={this.handlerClick}

Q: 上面那種方式最好?redux

構造函數bind > 箭頭函數 > 直接bind
由於構造函數只會在app建立,執行一次,箭頭函數和bind每次都返回一個新的函數,引發渲染

Q: React事件機制,爲何拿不到this數組

class Foo {
      constructor(name){
        this.name = name
    }
    
    display(){
        console.log(this.name);
      }
    }
    
    var foo = new Foo('Saurabh');
    foo.display(); // Saurabh
    
    //下面的賦值操做模擬了上下文的丟失。 
    //與實際在 React Component 中將處理程序做爲 callback 參數傳遞類似。
    // 類聲明和類表達式的主體以 嚴格模式 執行,主要包括構造函數、靜態方法和原型方法。Getter 和 setter 函數也在嚴格模式下執行。
    var display = foo.display; 
    display(); // TypeError: this is undefined

Q:爲何在使用bind能解決數據結構

// 解決:構造函數中bind,對應第三種方法
class Foo {
  constructor(name){
    this.name = name
    this.display = this.display.bind(this);
}

display(){
   console.log(this.name);
}

var foo = new Foo('Saurabh');
foo.display(); // Saurabh

var display = foo.display;
display(); // Saurabh

// 咱們也能夠在其餘地方bind,對應第一種方法
// 但因爲構造函數是全部初始化發生的地方,所以它是編寫綁定事件語句最佳的位置
class Foo {
  constructor(name){
    this.name = name;
  }

  display(){
    console.log(this.name);
  }
}

var foo = new Foo('Saurabh');
foo.display = foo.display.bind(foo);
foo.display(); // Saurabh

var display = foo.display;
display(); // Saurabh

Q:箭頭函數爲何能解決app

1.箭頭函數機制less

箭頭函數沒有 this,因此須要經過查找做用域鏈來肯定 this 的值。
這就意味着若是箭頭函數被非箭頭函數包含,this 綁定的就是最近一層非箭頭函數的 this
this 是有詞法約束力的。這意味它可使用封閉的函數上下文或者全局上下文做爲 this 的值

2.分析爲何能解決 - 箭頭函數兩種解決方式dom

公共類字段語法: 箭頭函數被包含在 Foo 類中或者構造函數中,因此它的上下文就是組件實例
class Foo extends React.Component{
  handleClick = () => {
    console.log(this); 
  }
 
  render(){
    return (
      <button type="button" onClick={this.handleClick}>
        Click Me
      </button>
    );
  }
}

ReactDOM.render(
  <Foo />,
  document.getElementById("app")
);
回調中的箭頭函數:箭頭函數被包含在 render() 方法中,該方法由 React 在組件實例的上下文中調用
class Foo extends React.Component{
 handleClick(event){
    console.log(this);
  }
 
  render(){
    return (
      <button type="button" onClick={(e) => this.handleClick(e)}>
        Click Me
      </button>
    );
  }
}

ReactDOM.render(
  <Foo />,
  document.getElementById("app")
);

Q: bind爲什麼不能用 call和apply這種替代函數

由於call和apply會當即執行,這是bind與call,apply的區別

2.傳遞參數注意性能

若是直接寫一個對象在item處,則每次會生成新的
so, 傳遞參數的時候要少傳,且每次傳遞的儘可能是一份數據,不要每次這個數據都會被從新建立優化

const item = { firstName: 'Liu' }
<Demo style={{ color: 'red' }} name={item}></Demo>

<Demo {...this.state}></Demo>

多組件優化

1.shouldComponentUpdate

// 淺比較先後兩次的props的變化
// 通常redux的state層次深,數據結構複雜,深層比較太消耗性能,得不償失
class Demo extends React.component {
  shouldComponentUpdate(nextProps, nextState) {
    if (compareObj(nextProps, this.props)) {
      return false
    }
    return true
  }

  render() {
    return <h2>{this.props.title}</h2>
  }
}

淺比較

function compareObj(obj1, obj2) {
  if (obj1 == obj2) {
    return true
  }
  if (Object.keys(obj1).length !== Object.keys(obj2).length) {
    return false
  }
  for(let k in obj1) {
    if (obj1[k] !== obj2[k]) {
      return false
    }
  }
  return true
}

補充: 深比較的實現 - 淺比較的遞歸

function compareObj(obj1, obj2) {
  if (obj1 == obj2) {
    return true
  }
  if (Object.keys(obj1).length !== Object.keys(obj2).length) {
    return false
  }
  for (let k in obj1) {
    if (typeof obj1[k] == 'object') {
      return compareObj(obj1[k], obj2[k])
    } else if (obj1[k] !== obj2[k]) {
      return false
    }
  }
  return true
}

React16 簡易的寫法 - 把nextProps每一個都遍歷比較了一遍

class Demo extends React.PureComponent {
  render() {
    return <h2>{ this.props.title }</h2>
  }
}

2. immutablejs

解決什麼問題:

immutablejs的出現就是爲了解決可變數據結構深層比較的性能問題
讓咱們更優更高效的比對數據,減小渲染

優點:

節省內存,下降可變的風險,能夠用等號判斷是否相等
比較是直接拿地址得hash作對比,因此比較相等的複雜度特別低

使用:

將react, redux的數據所有改成使用immutable的數據類型來操做
// 此時,定製shouldComponentUpdate就會特別簡單高效
class Demo extends React.component {
  shouldComponentUpdate(nextProps, nextState) {
    return is(nextProps, this.props)
  }

  render() {
    return <h2>{this.props.title}</h2>
  }
}

推薦immutable輕量級包:seamless-immutable

Key

建議:循環時,不要使用index來做爲key,最好拿數值或者數組與其餘的組合來確保key的惟一性

<ul>
  {this.state.users.map((v,index) => <li key={index}>{v}</li>)}
</ul>

問題:

若是我再數組前面插入數組,則整個數組的index都會發生變化,v-dom就沒有存在的意義
key用來標識同級的dom元素,其做用和重要性,詳情請見另外一篇blog - 深刻diff和虛擬dom
key變化了,react比對時,就會所有刪除插入,不會進行復用移動