0%

reselect源码分析

reselect 是什么

reselect 是 redux 的一个中间件,它用于创建 selectors,根据官方仓库的说明

Selectors can compute derived data, allowing Redux to store the minimal possible state.
选择器可以计算派生数据,从而允许 Redux 存储尽可能少的状态。
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 项目仓库地址

reselect 的源码只有 100 行。可以说是非常简单易懂的,但是以我目前的水平,阅读起来还是略有吃力,函数式编程就是绕啊。

尝试了一下将代码拷贝到本地 vscode 中进行调试运行,效果感觉还行,运行了几遍,整个流程就搞懂了。感觉这种小的工具库都可以用这种方式去分析它的源码。

1
2
3
export const createSelector = /* #__PURE__ */ createSelectorCreator(
defaultMemoize
);

这是这个库暴露的主要的 api,它是由 createSelectorCreator 这个工厂函数创建出来的,他接受一个 memoize 函数,同时可以接受选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
export function createSelectorCreator(memoize, ...memoizeOptions) {
return (...funcs) => {
let recomputations = 0;
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);

// If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
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;
};
}

下面默认的 momize 的实现,momize 接受一个函数,通过闭包保存上一次调用该函数的参数和结果,如果参数相等,直接返回结果。
默认的实现的相等比较是浅相等,对于对象比较的是它们的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
let lastArgs = null;
let lastResult = null;
// we reference arguments instead of spreading them for performance reasons
return function () {
if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {
// apply arguments instead of spreading for performance.
lastResult = func.apply(null, arguments);
}

lastArgs = arguments;
return lastResult;
};
}

在返回的 selector 和 resultFunc 的计算中,使用到了 momize。这个库做了两次的记忆化处理,第一层是如果 selector 接受的参数相等,那么就不再计算 resultFunc 的依赖。第二层是 resultFunc 的依赖计算完成后,如果与之前计算的结果相同,不会再次执行 resultFunc。