reselect是配合redux
使用的一款轻量型的状态选择库,目的在于当store
中的state
从新改变以后,使得局部未改变的状态不会由于总体的state
变化而所有从新渲染,功能有点相似于组件中的生命周期函数shouldComponentDidUpdate
,可是它们并非一个东西。下面是官方的一些简介:javascript
- 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
,并不表明强耦合。java
store
状态树庞大且层次较深我的认为符合这两点就可使用reselect
,为何?简单的state
或许根本彻底没有必要引入redux
,状态管理组件内部就能够消化,再者reselect
只是在参数级别的缓存,若是组件状态逻辑并非特别复杂,只是简单的getter
,那也可没必要引入reselect
。git
[建议]:建议引入了redux
就能够引入reselect
,去看官方的源码,总共加起来才短短的108行代码,对测试并无什么成本,同时加入也不会对打包体积形成什么影响,可是有些时候对组件渲染的性能却有很大的改善。github
这里是直接copy的官方仓库中的代码redux
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 } 复制代码
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
比较两次传入的参数是否相等,注意,这里是浅比较,即第一层引用的比较markdown
function defaultEqualityCheck(a, b) { return a === b } 复制代码
当两次传入的值存在变化的时候,那么就会执行app
func.apply(null, arguments) 复制代码
这里会计算获得全部的依赖,而后获得下一轮缓存函数的params
。less
就redux
的reducer
来说,这层缓存并无什么做用,看看reducer
代码:ide
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
。
好了,就啰嗦这么多了,最后,多读书,多看报,少吃零食,多睡觉😪😪💤