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
。
好了,就囉嗦這麼多了,最後,多讀書,多看報,少吃零食,多睡覺😪😪💤