React 组件的性能优化

一、单个 React 组件的性能优化

  • chrome 插件 React Pref
  • render: ‘组件渲染出什么’
  • shouldComponentUpdate:‘什么时候不需要重新渲染’
  • 善于使用 shouldComponentUpdate

二、多个 React 组件的性能优化

  • React 的调和(Reconciliation)过程
  • 找出原有的 Virtual DOM 和新生成的 Virtual DOM
  • React 在更新中这个‘找不同’的过程,就叫做调和(Reconciliation)
  • 对比两个 N 个节点的属性结构的算法,时间复杂度是O(N^3)
  • React 实际采用的算法需要的时间复杂度是 O(N)-----性能和复杂度的最好折中
    • 节点类型不同的情况—意味着改动太大
      • 原有的树形上的 React 组件会经历“卸载”的生命周期 (componentWillUnmount)
      • 取而代之的组件会经历装载过程的生命周期(componentWillMount、render、componentDidMount 依次调用)
    • 节点类型相同的情况
      • 原来的跟节点只需要更新过程
      • 节点类型两类: DOM 元素类型(HTML 直接支持的元素类型)、React 组件
      • DOM 元素:只修改发生变化的部分,让 DOM 操作尽可能少
      • React 组件类型:更新过程: componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
      • 重视 shouldComponentUpdate, 如果发现没必要重新渲染直接返回 false
  • Key 的用法
    • 告诉 React 每个组件的唯一标识(key 属性)
    • key 值不止唯一,还需要稳定不变
    • 用数组下标作为 key ,看起来唯一,但是却不是稳定不变的

三、利用 reselect 提高数据选取的性能

  • reselect 库的工作原理: 只要相关状态没有改变,那就直接使用上一次的缓存结果
  • 用来创造“选择器”:就是接受一个 state 作为参数的函数,这个选择器函数返回的数据就是我们某个 mapStateToProps 需要的结果
  • 两个步骤
    • 1、从输入参数 state 抽取第一层结果,将这第一层结果和之前抽取的第一层结果做比较,如果发现完全相同,就没必要进行第二部分运算了,选择器直接吧之前第二部分的运算结果返回就可以 (注:这一部分做的 “比较”就是 Javascript 的 === 操作符比较,如果第一层结果是对象的话,只有是同一个对象才会被认为是相同的)
    • 2、根据第一层结果计算出选择器需要返回的最终结果
      • 步骤一都会被执行,根据步骤一的结果来判断是否可以使用缓存的结果,是否需要执行第二步。
    • 使用方式:
    • npm install --save reselect
    • 以 todos 为例:
import { createSelector } from 'reselect'
import { FilterTypes } from '../constants.js'

export const selectVisibleTodos = createSelector(
    [getFilter, getTodos],
    (filter, todos) => {
        switch (filters) {
            case FilterTypes.All:
                return todos
            case FilterTypes.COMPLETED:
                return todos.filter(item => item.completed)
            case FilterTypes.UNCOMPLETED:
                return todos.filter(item => !item.completed)
            default:
                throw new Error('unsupported filter')
        }
    }
)

const getFilter = (state) => state.filter
const getTodos = (state) => state.todos
  • 第一个参数是一个函数数组,每个元素代表了选择器步骤一需要做的映射计算,这里我们提供了两个函数 getFilter、getTodos
  • 第二个参数代表步骤二的计算过程,参数为第一个参数的输出结果
  • 虽然 reselect 的 createSelector 创造的焉择其并不是一个纯函数,但是 createSelector 接受的搜有函数参数都是纯函数,虽然选择器有“记忆”这个副作用,到那时只要输入参数 state 没有变化,产生的结果也就没有变化,表现的却类似于纯函数
  • 例如例子里的,只要 Redux Store 状态树上的 filter 和 todos 字段不变,无论怎样触发 TodoList 的渲染过程,都不会引发没有必要的遍历 todos 字段的运算,性能自然更快
  • 范式化的状态树
    • 范式化: 遵照关系型数据库的设计原则,减少冗余数据
    • 反范式化:利用数据冗余换取读写效率
    • 例子:给 Todo 增加一个 Type 概念
// 反范式化
{
    id: 1, // 待办事项 id
    text: '待办事项1', // 待办事项文字内容
    completed: false, // 是否已完成
    type: { // 种类
        name: '紧急', // 种类名称
        color: 'red' // 种类显示颜色
    }
}
  • 反范式化
    • 优点: 从 Redux Store 上获得的状态可以直接使用name 和 color 数据
    • 缺点:需要改变某种类型的名称和颜色是,需要遍历所有的 TodoItem来完成
    • 特点: 读取容易,修改麻烦
// 范式化
{
    id: 1, // 待办事项 id
    text: '待办事项1', // 待办事项文字内容
    completed: false, // 是否已完成
    typeId: 1 // 待办事项所属的种类 id
    
}
{
    id: 1, // 种类 id
    name: '紧急', // 种类名称
    color: 'red' // 种类显示颜色
}
  • 范式化
    • 优点: 修改某个种类的名称或者颜色,只需要修改types 中的一处数据就行
    • 缺点: 获取数据时,需要做一个类似关系型数据库的 join 操作
  • 两者对比,范式方式更合理,虽然 join 数据需要花时间,但是应用了 reselect 之后,大部分情况都会命中缓存,实际上也就没有花费很多计算时间了。

总结:

  • 避免传递给其他组件的prop 值是一个不同的对象,不然会造成无谓的重复渲染
  • 不能随意修改一个作为容器的 HTML 节点类型
  • 对于动态数量的同类型子组件,一定要使用 key 这个 prop
  • React 的 Reconciliation 算法缺点是无法发现某个子树移动位置的情况,如果某个子树移动了位置,那 React 就会重新创建这个子树。
  • 利用 reselect 库来实现高效的数据获取。Redux Store 的状态树应该按照范式化原则来设计,减少数据冗余,这样利于保持数据一致。

该文章是对《深入浅出 React 和 Redux 》一书中的第五章

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值