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
比較兩次傳入的參數是否相等,注意,這裏是淺比較,即第一層引用的比較app
function defaultEqualityCheck(a, b) {
return a === b
}
複製代碼
當兩次傳入的值存在變化的時候,那麼就會執行less
func.apply(null, arguments)
複製代碼
這裏會計算獲得全部的依賴,而後獲得下一輪緩存函數的params
。ide
就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
。
好了,就囉嗦這麼多了,最後,多讀書,多看報,少吃零食,多睡覺😪😪💤