目录
- [1,applyMiddleware 原理](#1,applyMiddleware 原理)
- 2,实现
-
- 2.1,applyMiddleware
-
- [2.1.1,compose 方法](#2.1.1,compose 方法)
- 2.1.2,applyMiddleware
- [2.2,修改 createStore](#2.2,修改 createStore)
接上篇文章:Redux中间件介绍。
1,applyMiddleware 原理
Redux 应用中间件方式:
js
const store = createStore(reducer, applyMiddleware(logger1, logger2));
实际会转为下面的方式执行:
js
const store = applyMiddleware(logger1, logger2)(createStore)(reducer)
applyMiddleware
会确定用到的中间件;它会返回一个用来创建仓库的函数A,参数 是createStore
;- 函数 A,会返回创建仓库的函数B,和
createStore
函数差不多。 - 函数B,会对中间件函数做处理,并修改原始
dispatch
。
大致相当于:
js
export const applyMiddleware = (...middlewares) => {
// 用来创建的仓库的函数
return (createStore) => {
// 创建仓库的函数
return (reducer, defaultState) => {
const store = createStore(reducer, defaultState)
const dispatch = '经过 middlewares 处理的 store.dispatch'
return {
...store,
dispatch
}
}
}
}
2,实现
2.1,applyMiddleware
上篇文章介绍了 中间件函数 的写法和多个中间件函数的执行顺序。
基于此,实现这个 dispatch
流转的逻辑,并得到最终的 dispatch
即可完成 applyMiddleware
。
2.1.1,compose 方法
关键要实现
js
const resultDispatch = logger1(logger2(logger3(store.dispatch)))
实现:
js
/**
* @param {...any} funcs
* @returns {function}
*/
export const compose = (...funcs) => {
if (funcs.length === 0) {
return (args) => args;
} else if (funcs.length === 1) {
return funcs[0];
}
return (...args) => {
let lastReturn = null;
for (let i = funcs.length - 1; i >= 0; i--) {
const func = funcs[i];
if (i === funcs.length - 1) {
lastReturn = func(...args);
} else {
lastReturn = func(lastReturn);
}
}
return lastReturn;
};
};
js
// 测试代码
const add = (n) => {
return n + n;
};
const mult = (n) => {
return n * n;
};
const b = compose(add, mult);
console.log(b(3)); // 先乘后加,18
可使用 Array.reduce
简化:
js
export const compose = (...funcs) =>
funcs.reduce(
(prev, next) =>
(...args) =>
prev(next(...args))
);
2.1.2,applyMiddleware
js
import { compose } from "./compose";
export const applyMiddleware = (...middlewares) => {
return (createStore) => {
return (reducer, defaultState) => {
const store = createStore(reducer, defaultState);
let dispatch = () => {
throw new Error("目前还不能使用 dispatch");
};
// 传递给中间件函数的 store 只有这2个属性。
const simpleStore = {
getState: store.getState,
dispatch: (...args) => dispatch(...args), // 每个中间件函数的 dispatch 都是上一个中间件组装后的
};
// 获取用于创建 dispatch 的函数
const dispatchProducts = middlewares.map((m) => m(simpleStore));
// 重新组装后的 dispatch
dispatch = compose(...dispatchProducts)(store.dispatch);
return {
...store,
dispatch,
};
};
};
};
2.2,修改 createStore
之前实现的 createStore,没有对可能的第3个函数做处理。这里补充下:
- 如果第2个参数是
applyMiddleware
,那说明没有defaultState
。
这里就简单判断写第2个参数是不是函数,实际源码中
defaultState
也可以通过一个函数创建。
js
export const createStore = (reducer, defaultState, enhancer) => {
// enhancer 表示 applymiddleware 返回的函数。
if (typeof defaultState === 'function') {
enhancer = defaultState
defaultState = undefined
}
if (typeof enhancer === 'function') {
enhancer(createStore)(reducer, defaultState)
}
// 其他剩余代码没有做变动。
// ...
}
完整代码
js
export const createStore = (reducer, defaultState, enhancer) => {
// enhancer 表示 applymiddleware 返回的函数。
if (typeof defaultState === "function") {
enhancer = defaultState;
defaultState = undefined;
}
if (typeof enhancer === "function") {
enhancer(createStore)(reducer, defaultState);
}
let currentReducer = reducer;
let currentState = defaultState;
let listeners = [];
const dispatch = (action) => {
if (typeof action !== "object" || Object.getPrototypeOf(action) !== Object.prototype) {
throw new Error("action 必须是一个 plain Object");
}
if (action.type === undefined) {
throw new Error("action 必须有 type 属性");
}
currentState = currentReducer(currentState, action);
// 每次更新时,遍历监听器。
for (const listener of listeners) {
listener();
}
};
const getState = () => {
return currentState;
};
const subscribe = (listener) => {
listeners.push(listener);
let isRemove = false;
// 取消监听
return () => {
if (isRemove) {
return;
} else {
isRemove = true;
listeners = listeners.filter((f) => f !== listener);
}
};
};
// createStore 创建时会调用一次。
dispatch({
type: `@@redux/INIT${getRandomString}`,
});
return {
dispatch,
getState,
subscribe,
};
};
function getRandomString() {
return Math.random().toString(36).substring(2, 8).split("").join(".");
}
以上。