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

更新日期: 2019-12-07阅读: 1.9k标签: 渲染

reselect是什么?

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的官方仓库中的代码

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是怎么优化代码性能的?

整体store层级state的缓存

组件级别state的缓存

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),这里缓存的逻辑跟上面没什么区别,这里就不在讲解了。resultFunc是createSelector中的最后一个参数

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。

原文:https://www.cnblogs.com/rynxiao/p/12020844.html

链接: https://www.fly63.com/article/detial/6818

vue中数据更新变化,而页面视图未渲染的解决方案

在使用vue的时候,我们都知道它是双向数据绑定的,但是在使用不熟的情况下,经常会遇到:data中的数据变化了,但是并没有触发页面渲染。下面就整理一些出现这种情况的场景以及解决办法。

服务端渲染和客户端渲染的对比

这里结合art-template模板引擎说明。首先了解下前端页面中如何使用art-template。当不需要对SEO友好的时候,推荐使用客户端渲染;当需要对 SEO友好的时候,推荐使用服务器端渲染

解决使用vue.js未渲染前代码显示问题

在使用vue的时候,偶然发现多次刷新或者网络加载缓慢的时候,会一瞬间出现设置的模板的情况。实在很影响美观,可以使用vue现成的指令来解决这个问题:v-cloak

在微信小程序中渲染html内容的实现

大部分Web应用的富文本内容都是以HTML字符串的形式存储的,通过HTML文档去展示HTML内容自然没有问题。但是,在微信小程序(下文简称为「小程序」)中,应当如何渲染这部分内容呢?

原来 CSS 与 JS 是这样阻塞 DOM 解析和渲染的

估计大家都听过,尽量将 CSS 放头部,JS 放底部,这样可以提高页面的性能。然而,为什么呢?大家有考虑过么?很长一段时间,我都是知其然而不知其所以然,强行背下来应付考核当然可以,但实际应用中必然一塌糊涂

Vue渲染数据理解以及Vue指令

原生JS改变页面数据,必须要获取页面节点,也即是进行DOM操作,jQuery之类的框架只是简化DOM操作的写法,实质并没有改变操作页面数据的底层原理,DOM操作影响性能(导致浏览器的重绘和回流),Vue是一个mvvm框架(库),大幅度减少了DOM操作

Web渲染那些事儿

在决定渲染方式时,需要测量和理解真正的瓶颈在哪里。静态渲染或服务器渲染在多数情况都比较适用,尤其是可交互性对JS依赖较低的场景。下面是一张便捷的信息图,显示了服务器到客户端的技术频谱:

vue从后台获取数据赋值给data,如何渲染更细视图

如果从服务端返回的数据量较少,或者只有几个字段,可以用vue的set方法,如果数据量较大,请直接看第二种情况。官网API是这样介绍的:Vue.set(target,key,value)

react 异步加载数据时的渲染问题

当数据需要异步加载时render获取不到数据可能会报一些错误,此时需要在render函数中加一个判断.行到render时,state对象的haveData为false, 所以此时页面展示 loading,当异步获取数据成功时

Vue.js中v-html渲染的dom添加scoped的样式

在vue.js中,要将一段字符串渲染成html,可以使用v-html指令。但是 官方文档 中的v-html部分也提醒了

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!