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

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

总结

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

reselect是怎样提升react组件渲染性能的?的更多相关文章

  1. react+redux渲染性能优化原理

    大家都知道,react的一个痛点就是非父子关系的组件之间的通信,其官方文档对此也并不避讳: For communication between two components that don't ha ...

  2. React + Reflux 渲染性能优化原理

    作者:ManfredHu 链接:http://www.manfredhu.com/2016/11/08/23-reactRenderingPrinciple 声明:版权所有,转载请保留本段信息,否则请 ...

  3. 使用CSS3开启GPU硬件加速提升网站动画渲染性能

    遇到的问题: 网站本身设计初衷就没有打算支持IE8及以下版本浏览器,并不是因为代码兼容性问题,而是真的不想迁就那些懒得更新自己操作系统和浏览器的用户,毕竟是我自己的网站,所以我说了算!哈哈~ 没有了低 ...

  4. 相当有用的react+redux渲染性能优化原理

    学习地址:http://foio.github.io/react-redux-performance-boost/

  5. React组件性能调优

    React是一个专注于UI层的框架,它使用虚拟DOM技术,以保证它UI的高速渲染:使用单向数据流,因此它数据绑定更加简单:那么它内部是如何保持简单高效的UI渲染呢?这种渲染机制有可能存在什么性能问题呢 ...

  6. React组件性能优化

    转自:https://segmentfault.com/a/1190000006100489 React: 一个用于构建用户界面的JAVASCRIPT库. React仅仅专注于UI层:它使用虚拟DOM ...

  7. 从性能角度看react组件拆分的重要性

    React是一个UI层面的库,它采用虚拟DOM技术减少Javascript与真正DOM的交互,提升了前端性能:采用单向数据流机制,父组件通过props将数据传递给子组件,这样让数据流向一目了然.一旦组 ...

  8. React 组件性能优化探索实践

    转自:http://www.tuicool.com/articles/Ar6Zruq React本身就非常关注性能,其提供的虚拟DOM搭配上Diff算法,实现对DOM操作最小粒度的改变也是非常的高效. ...

  9. React组件:拖拽布局Dragact v0.1.6 发布

    仓库地址:Dragact爽滑的拖拽组件 大家好,新年已经过去,大家又投入了繁忙的工作当中,由于我在国外,因此压根儿没有休息... 少说废话,上周一周的时间里,我陆陆续续的为Dragact组件进行了一系 ...

随机推荐

  1. day02_Requests模块

    1.anaconda的安装 1.1 .安装可执行程序 1.2 .配置环境变量 ​ 根据环境变量的先后顺序去查找可执行程序文件,如果查找到就执行,如果查找不到就报错. ​ anaconda主要配置: ​ ...

  2. Selenium+Java(二)Selenium打开IE浏览器

    前言 已在Eclipse中配置完成Selenium的相关配置,不知道如何配置的可参考我的另一篇博文:https://www.cnblogs.com/yogouo/p/11946940.html 打开I ...

  3. python字符串删除,列表删除以及字典删除的总结

    一:字符串删除  1,字符串本身是不可变的,一个字符串定义以后,对他本身是不能做任何操作的,所以的增删改都是对原字符串拷贝的副本的操作,原来的字符串还是原来的字符串,它本身并没 有变 2,字符串本身是 ...

  4. 阿里云上万个 Kubernetes 集群大规模管理实践

    点击下载<不一样的 双11 技术:阿里巴巴经济体云原生实践> 本文节选自<不一样的 双11 技术:阿里巴巴经济体云原生实践>一书,点击上方图片即可下载! 作者 | 汤志敏,阿里 ...

  5. easywechat微信开发SDK之小微商户进件(一)

    微信本身不提供小微商户进件的SDK,偶然发现easywechat这么个东西,官网地址是https://www.easywechat.com/  整合了微信开发中常用的接口,包括微信公众号相关接口,微信 ...

  6. 痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU启动那些事(4)- OTP及其烧写方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是恩智浦i.MX RTxxx系列MCU的OTP. 在i.MXRTxxx启动系列第二篇文章 Boot配置(ISP Pin, OTP) 里痞子 ...

  7. LNMP Shell脚本发布

    #!/bin/bash # : #This author is DKS #auto install nginx mysql php ################################## ...

  8. JS的Form表单转JSON格式

    一.serialize()方法 格式:var data = $("#formID").serialize(); 功能:将表单内容序列化成一个字符串. 注意:要使用params = ...

  9. 微信小程序——事件冒泡

    在微信小程序的事件分为冒泡事件和非冒泡事件: 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递. 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递. WXML的冒泡事件列表: ...

  10. 【开发记录】Linux常用命令记录(一)【华为云技术分享】

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...