React性能优化 -- 利用React-Redux

React渲染机制

前面这篇文章,我已经介绍了React的渲染机制.html

简单来讲,就是在页面一开始打开的时候,React会调用render函数构建一棵Dom树,在state/props发生改变的时候,render函数会被再次调用渲染出另一棵virtula Dom,接着,React会用对两棵树进行对比,计算出对DOM树的最少修改,而后批量改动。

找出优化点

注意这里面,若是能够在渲染virtual DOM以前就能够判断渲染结果不会有变化,那么能够直接不进行virtual DOM的渲染和比较,速度回更快。react

shouldComponentUpdate

怎么能够作到呢?redux

这里须要用到React生命周期里的shouldComponentUpdate,当这个函数返回false的时候,DOM tree直接不须要从新渲染,从而节省大量的计算资源。
因为每一个React组件的逻辑都有本身的特色,因此须要根据组件逻辑来定制shouldComponentUpdate函数的行为.segmentfault

以官网的TodoList为例:
http://cn.redux.js.org/docs/b...函数

import React, { Component, PropTypes } from 'react'

export default class Todo extends Component {
  render() {
    return (
      <li
        onClick={this.props.onClick}
        style={{
          textDecoration: this.props.completed ? 'line-through' : 'none',
          cursor: this.props.completed ? 'default' : 'pointer'
        }}>
        {this.props.text}
      </li>
    )
  }
}

Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  text: PropTypes.string.isRequired,
  completed: PropTypes.bool.isRequired
}

对于这个todo来讲,只要completed跟text都没有发生改变,那么这个组件渲染的结果就不会发生改变,所以,shouldComponentUpdate就能够这样写:优化

shouleComponentUpdate(nextProps, nextState) {
  return (nextProps.completed !== this.props.completed) || 
         (nextProps.text !== this.props.text)
}

两个问题

接下来就有两个问题须要咱们思考了:ui

  1. 是否是全部的组件都须要这个shouldComponentUpdate
  2. 每一个须要shouldComponentUpdate的组件都须要本身写逻辑是否是太麻烦了

问题一:

关于这个问题,我在前一篇文章其实已经做答,使用React Pref,或者why-did-you-update均可以找到无需被从新渲染的组件,这个组件就是须要使用shouldComponetUpdate优化的组件。this

问题二:

确实麻烦啊,能偷懒就偷懒的咱们怎么能忍受。
因此能够直接使用React-Redux的connect帮助咱们.spa

React-Redux的connect其实会自动作一个对props的优化比较。过程以下:3d

clipboard.png

简而言之,它会根据传入进来的props,state经过各类计算获得nextProps跟上一个props作比较,若是不相等,shouldComponentUpdate才会返回false。

因此,有了它,todo.js就能够这样写

import React, { Component, PropTypes } from 'react'
class Todo extends Component {
  render() {
    return (
      <li
        onClick={this.props.onClick}
        style={{
          textDecoration: this.props.completed ? 'line-through' : 'none',
          cursor: this.props.completed ? 'default' : 'pointer'
        }}>
        {this.props.text}
      </li>
    )
  }
}


Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  text: PropTypes.string.isRequired,
  completed: PropTypes.bool.isRequired
}

export default connect()(Todo);

跟踪问题

这里我使用React Pref跟踪问题,加上以后,查看控制台,能够看到浪费的渲染时间由TodoList -> Todo转变到了Connect(Todo)> Todo,要理解这个现象就要理解connect里shouldComponentUpdate的实现方式。

注意:

clipboard.png

这里对props的对比只是一个浅比较,因此要让react-redux认为先后的对象是相同的,必须指向同一个js对象。

例如:

<Foo style={color: 'red'}>

这样每次传入进来的style都是一个新的对象,因此即便里面的值相等,react-redux的浅比较仍然认为它不等须要从新渲染。

再回来看看TodoList里是怎么用Todo的

<ul>
    {this.props.todos.map((todo, index) =>
      <Todo {...todo}
            key={index}
            onClick={() => this.props.onTodoClick(index)} />
    )}
 </ul>

不知道你们发现了没有?这里的每个onClick都是一个新的函数,即便Todo被装备了shouldComponentUpdate的实现,浅比较的时候props老是不相等,依旧躲不过每次更新都要被从新渲染的命运。

如何改进

有两种作法:

方法一:

先来看看这个onTodoClick是怎么被一层层传下来的:

// App.js
<TodoList
  todos={visibleTodos}
  onTodoClick={index =>
  dispatch(toggleTodo(index))}
/>

// TodoList.js
 <Todo {...todo}
   key={index}
   onClick={() => this.props.onTodoClick(index)}
/>

//todo.js
 <li
    onClick={this.props.onClick}
    style={{
      textDecoration: this.props.completed ? 'line-through' : 'none',
      cursor: this.props.completed ? 'default' : 'pointer'
    }}>
    {this.props.text}
  </li>

能够在TodoList的时候,不构造匿名函数直接将onTodoClick传下来,而后index能够放在新加的属性index里。

// TodoList.js
 <Todo {...todo}
   key={index}
   *id={item.id}*
   onClick={onTodoClick}
/>

//todo.js的mapDispatchToProps须要作对应的改变

const mapDispatchToProps = (dispatch, ownProps) => ({
  onClick: () => ownProps.onToggle(ownProps.id)
})

方法二:
直接让TodoList不要给todo传递任何函数类型的prop,点击事件彻底由todo组件本身搞定。

// TodoList只须要穿一个id出来
 <Todo {...todo}
   key={index}
   *id={item.id}*
/>

// todo中,本身经过react-redux派发action便可

const mapDispatchToProps = (dispatch, ownProps) => {
  const {id} = this.props;
  return {
    onClick: () => dispatch(toggleTodo(id))
  }
}

对比两种方式,其实对于todo来讲,都须要使用react-redux,都须要todoList传入一个id,区别只在于actions是由父组件仍是有组件本身导入。

相比而言,没有必要一层层传递这个action,第二种方式让todo处理本身的一切事务,更符合高内聚的要求。

总结

讲了那么多,总之就是经过React Pref帮咱们找到须要优化的组件,而后用connect帮助咱们作优化偷个懒。

参考:<深刻浅出React和Redux> -- 程墨

相关文章
相关标签/搜索