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.uj5u.com/qiye/164083.html
標籤:JavaScript
