当前位置: 移动技术网 > IT编程>开发语言>JavaScript > reselect是怎样提升react组件渲染性能的?

reselect是怎样提升react组件渲染性能的?

2019年12月11日  | 移动技术网IT编程  | 我要评论
reselect是什么? "reselect" 是配合 使用的一款轻量型的状态选择库,目的在于当store中的state重新改变之后,使得局部未改变的状态不会因为整体的state变化而全部重新渲染,功能有点类似于组件中的生命周期函数 ,但是它们并不是一个东西。下面是官方的一些简介: Selector ...

reselect是什么?

是配合redux使用的一款轻量型的状态选择库,目的在于当store中的state重新改变之后,使得局部未改变的状态不会因为整体的state变化而全部重新渲染,功能有点类似于组件中的生命周期函数shouldcomponentdidupdate,但是它们并不是一个东西。下面是官方的一些简介:

  • selectors can compute derived data, allowing redux to store the minimal possible state.
  • selectors are efficient. a selector is not recomputed unless one of its arguments changes.
  • selectors are composable. they can be used as input to other selectors.

[注]:并不是reselect非要和redux绑定使用不可,可以说reselect只是一个enhancement,并不代表强耦合。

什么时候用reselect?

  • store状态树庞大且层次较深
  • 组件中的state需要经过复杂的计算才能呈现在界面上

个人认为符合这两点就可以使用reselect,为什么?简单的state或许根本完全没有必要引入redux,状态管理组件内部就可以消化,再者reselect只是在参数级别的缓存,如果组件状态逻辑并不是特别复杂,只是简单的getter,那也可不必引入reselect。

[建议]:建议引入了redux就可以引入reselect,去看官方的源码,总共加起来才短短的108行代码,对测试并没有什么成本,同时加入也不会对打包体积造成什么影响,但是有些时候对组件渲染的性能却有很大的改善。

基本用法

这里是直接copy的官方仓库中的代码

edit reselect

import { createselector } from 'reselect'

const shopitemsselector = state => state.shop.items
const taxpercentselector = state => state.shop.taxpercent

const subtotalselector = createselector(
  shopitemsselector,
  items => items.reduce((acc, item) => acc + item.value, 0)
)

const taxselector = createselector(
  subtotalselector,
  taxpercentselector,
  (subtotal, taxpercent) => subtotal * (taxpercent / 100)
)

export const totalselector = createselector(
  subtotalselector,
  taxselector,
  (subtotal, tax) => ({ total: subtotal + tax })
)

let examplestate = {
  shop: {
    taxpercent: 8,
    items: [
      { name: 'apple', value: 1.20 },
      { name: 'orange', value: 0.95 },
    ]
  }
}

console.log(subtotalselector(examplestate)) // 2.15
console.log(taxselector(examplestate))      // 0.172
console.log(totalselector(examplestate))    // { total: 2.322 }

reselect是怎么优化代码性能的?

const selector = memoize(function () {
  const params = []
  const length = dependencies.length

  for (let i = 0; i < length; i++) {
    // apply arguments instead of spreading and mutate a local list of params for performance.
    params.push(dependencies[i].apply(null, arguments))
  }

  // apply arguments instead of spreading for performance.
  return memoizedresultfunc.apply(null, params)
})

selector.resultfunc = resultfunc
selector.dependencies = dependencies
selector.recomputations = () => recomputations
selector.resetrecomputations = () => recomputations = 0
return selector

函数整体返回的就是这个selector,因为我们调用createselector,其实返回的是一个函数,所以memoize返回的其实也是一个函数,那么selector中做了什么?

export function defaultmemoize(func, equalitycheck = defaultequalitycheck) {
  let lastargs = null
  let lastresult = null
  // we reference arguments instead of spreading them for performance reasons
  // 这里作为返回的函数,传入的参数即为state
  return function () {
    if (!areargumentsshallowlyequal(equalitycheck, lastargs, arguments)) {
      // apply arguments instead of spreading for performance.
      lastresult = func.apply(null, arguments)
    }

    lastargs = arguments
    return lastresult
  }
}

memoize是reselect中提供的默认缓存函数,可以的得知执行这个函数的时候,返回的函数即为上面代码中的selector,那么arguments即为传入的state,通过areargumentsshallowlyequal比较两次传入的参数是否相等,注意,这里是浅比较,即第一层引用的比较

function defaultequalitycheck(a, b) {
  return a === b
}

当两次传入的值存在变化的时候,那么就会执行

func.apply(null, arguments)

这里会计算得到所有的依赖,然后得到下一轮缓存函数的params

就redux的reducer来讲,这层缓存并没有什么作用,看看reducer代码:

function reducer(state, action) {
  switch (action.type): 
    case request_todo_pending:
        return { ...state, loading: true };
    case request_todo_list_success:
        return { ...state, list: ['todo'], loading: false };
    // ...
    // default
}

redux社区推崇所有的state都是不可变的,所以只要dispatch了一个action,每次返回的state必然会是一个新的对象,对于浅比较每次返回的结果必然是true;

所以,缓存的关键还在第二层momoize,因为这里的state并不是每一次都必须变化:

const resultfunc = funcs.pop()
const dependencies = getdependencies(funcs)

const memoizedresultfunc = memoize(
  function () {
    recomputations++
    // apply arguments instead of spreading for performance.
    return resultfunc.apply(null, arguments)
  },
  ...memoizeoptions
)

真正代码的执行在resultfunc.apply(null, arguments),这里缓存的逻辑跟上面没什么区别,这里就不在讲解了。resultfunccreateselector中的最后一个参数

const shopitemsselector = state => state.shop.items
const taxpercentselector = state => state.shop.taxpercent

const subtotalselector = createselector(
  shopitemsselector,
  items => items.reduce((acc, item) => acc + item.value, 0)
)

大家可以自行对照一下上面的这个例子,那么arguments就是第二个函数的参数,也就是第一步缓存函数中的params

总结

好了,就啰嗦这么多了,最后,多读书,多看报,少吃零食,多睡觉

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网