【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: 上面那种方式最好?

构造函数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:箭头函数为什么能解决

1.箭头函数机制

箭头函数没有 this,所以需要通过查找作用域链来确定 this 的值。
这就意味着如果箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 this
this 是有词法约束力的。这意味它可以使用封闭的函数上下文或者全局上下文作为 this 的值

2.分析为什么能解决 – 箭头函数两种解决方式

公共类字段语法: 箭头函数被包含在 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比对时,就会全部删除插入,不会进行复用移动
    原文作者:Rainie
    原文地址: https://segmentfault.com/a/1190000016816130
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞