【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比对时,就会所有删除插入,不会进行复用移动