React Redux 原理分析和部分源码解析

前言

面对 Redux 起初是感觉类似 Android Event Bus 的东西,用来做成事件总线,但是 Event Bus 是单纯的订阅发布的事件总线,和 Redux 还是有区别,他规范了数据流的概念,按照 MVC 模型会变得更为简洁,但是同时因为他的规范,也让工程复杂度提升了一点。
由于是类似 Event Bus 的东西,所以源码应该不会太复杂,我来看看源码有什么值得学习的东西先~

redux 里面有什么

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
34
35
36
37
export default function createStore(reducer, preloadedState, enhancer) {
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error(
'It looks like you are passing several store enhancers to ' +
'createStore(). This is not supported. Instead, compose them ' +
'together to a single function.'
)
}

if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}

if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}

return enhancer(createStore)(reducer, preloadedState)
}

if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}

let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false

...
}

先忽略过多的代码,首先看看最后的处理:

1
2
3
4
5
let currentReducer = reducer;
let currentState = preloadedState;
let currentListeners = [];
let nextListeners = currentListeners;
let isDispatching = false;

这边最后只是单纯的记录下传入的参数,和isDispatchingcurrentListenersnextListeners初始化,这里就大概能明白,其实核心还是一样的,订阅发布,话不多说,因为也有subscribe的概念,这边按照订阅发布来看看dispatch之后流程跟这几个的关系。

subscribe

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
34
35
36
37
38
function subscribe(listener) {
if (typeof listener !== "function") {
throw new Error("Expected the listener to be a function.");
}

if (isDispatching) {
throw new Error(
"You may not call store.subscribe() while the reducer is executing. " +
"If you would like to be notified after the store has been updated, subscribe from a " +
"component and invoke store.getState() in the callback to access the latest state. " +
"See https://redux.js.org/api-reference/store#subscribe(listener) for more details."
);
}

let isSubscribed = true;

ensureCanMutateNextListeners();
nextListeners.push(listener);

return function unsubscribe() {
if (!isSubscribed) {
return;
}

if (isDispatching) {
throw new Error(
"You may not unsubscribe from a store listener while the reducer is executing. " +
"See https://redux.js.org/api-reference/store#subscribe(listener) for more details."
);
}

isSubscribed = false;

ensureCanMutateNextListeners();
const index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
};
}

这里就比较简单,各种判断,是否发布中检查参数等等,最后增加到 Listeners 里面,同时返回一个取消订阅的方法,之后就看看dispatch怎样的。

dispatch

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
34
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
"Actions must be plain objects. " +
"Use custom middleware for async actions."
);
}

if (typeof action.type === "undefined") {
throw new Error(
'Actions may not have an undefined "type" property. ' +
"Have you misspelled a constant?"
);
}

if (isDispatching) {
throw new Error("Reducers may not dispatch actions.");
}

try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}

const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}

return action;
}

这边可以看到,其实还是订阅发布这样,交给Reducer之后,交给listener,这已经就是 Redux 核心功能了,之后看看平时用到的中间价如何实现的

applyMiddleware

我们已经看过了createStore(reducer, preloadedState, enhancer)代码,但是没看到中间件在哪实现,实际上这边上面的代码都是判断参数,还有就是兼容preloadedState, enhancer入参位置,其实enhancer就是实现中间件的地方,不得不说这代码很抽象,然后接着看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args);
let dispatch = () => {
throw new Error(
"Dispatching while constructing your middleware is not allowed. " +
"Other middleware would not be applied to this dispatch."
);
};

const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);

return {
...store,
dispatch
};
};
}

这里其实能看到最核心的功能就是改变了dispatch,但是这个compose是干嘛的?

1
2
3
4
5
6
7
8
9
10
11
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg;
}

if (funcs.length === 1) {
return funcs[0];
}

return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

实际上这个就是把中间件串行,一个一个执行然后下一个,(...args) => f(g(h(...args)))这个模样,所以中间件也就是这个意思,在真正dispatch前一个一个执行。

何谓中间件

但是应该会有一些只做前端的不太理解中间件,后端用得比较多,举个 🌰,当一个修改用户名的请求进入服务器,那么这个流程是怎样的呢?一般下意识我们会做各种校验,等等,但是用户相关接口不止一个,非常多,那怎么办呢?就是在进入到真正的 Controller 的之前做一层中间件,在这个地方校验,以后就不需要担心了,同理,前端的中间件也是这样,最形象的 🌰 就是为了解决dispatch不能传入方法的redux-thunk,他的代码十分简单,下面来看看。

redux-thunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === "function") {
return action(dispatch, getState, extraArgument);
}

return next(action);
};
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

完事了…就是判断action入参是否方法,是,传参,执行…

react-redux

根据上面的源码阅读,可以看到其实 Redux 十分简单,只提供了最核心的功能,但是他是如何将组件联系在一起的呢?我这边简单的看看

Provider的实现是Context,通过Context传递,组件获取到Contextstoresubscribe订阅之后更新。

总结

Redux 果然还是跟我想象中的一样,类似 Event Bus 的事件总线,代码又抽象,但是容易阅读,十分建议去学习一下。