Промежуточное ПО Redux предоставляется по адресуactionПосле начала, достичьreducerПредыдущая точка расширения, другими словами, изначальноview -> action -> reducer -> storeПосле добавления промежуточного ПО поток данных становитсяview -> action -> middleware -> reducer -> store, в этой ссылке мы можем выполнять некоторые операции с «побочными эффектами», такие как асинхронные запросы, печать логов и т. д.
Пример использования
Возьмем в качестве примера вывод журнала Logger:
import { createStore, applyMiddleware } from 'redux'
/** 定义初始 state**/
const initState = {
score : 0.5
}
/** 定义 reducer**/
const reducer = (state, action) => {
switch (action.type) {
case 'CHANGE_SCORE':
return { ...state, score:action.score }
default:
break
}
}
/** 定义中间件 **/
const logger = ({ getState, dispatch }) => next => action => {
console.log('【logger】即将执行:', action)
// 调用 middleware 链中下一个 middleware 的 dispatch。
let returnValue = next(action)
console.log('【logger】执行完成后 state:', getState())
return returnValue
}
/** 创建 store**/
let store = createStore(reducer, initState, applyMiddleware(logger))
/** 现在尝试发送一个 action**/
store.dispatch({
type: 'CHANGE_SCORE',
score: 0.8
})
/** 打印:**/
// 【logger】即将执行: { type: 'CHANGE_SCORE', score: 0.8 }
// 【logger】执行完成后 state: { score: 0.8 }
интерпретировать
Чтобы понять вышеуказанный код, вы должны сначала создатьstoreизcreateStoreНачнем с функции:createStoreФункция принимает параметры как(reducer, [preloadedState], enhancer),вpreloadedStateдля начальногоstate,ТакenhancerЧто это такое? Как видно из официальной документации,StoreCreatorСигнатура функции
type StoreCreator = (reducer: Reducer, initialState: ?State) => Store
это обычное творениеstoreфункция, иenhancerподпись
type enhancer = (next: StoreCreator) => StoreCreator
знатьenhancerпредставляет собой комбинациюStoreCreatorиз高阶函数, который возвращает новый расширенныйStoreCreator, а затем выполнитьStoreCreatorрасширенная версияstore。
В этом примере формальный параметрenhancerто естьapplyMiddleware, как видно из следующего исходного кода,applyMiddlewareпереписанныйstoreизdispatchметод, новыйdispatchТо есть он обернут входящим промежуточным ПО.
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
// 接收 createStore 参数
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch
var chain = []
// 传递给中间件的参数
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
// 注册中间件调用链,并由此可知,所有的中间件最外层函数接收的参数都是{getState,dispatch}
chain = middlewares.map(middleware => middleware(middlewareAPI))
//compose 函数起到代码组合的作用:compose(f, g, h)(...args) 效果等同于 f(g(h(...args))),具体实现可参见附录。从此也可见:所有的中间件最二层函数接收的参数为 dispatch,一般我们在定义中间件时这个形参不叫 dispatch 而叫 next,是由于此时的 dispatch 不一定是原始 store.dispatch,有可能是被包装过的新的 dispatch。
dispatch = compose(...chain)(store.dispatch)
// 返回经 middlewares 增强后的 createStore
return {
...store,
dispatch
}
}
}
Таким образом, исходная реализацияdispatch(action)Место становится выполнением новой функции
(action)=>{
console.log('【logger】即将执行:', action)
dispatch(action)
console.log('【logger】执行完成后 state:', getState())
}
Вот и всеaction -> reducerПерехват, поэтому каждый раз спусковой крючокactionВсе можно выйти из системы, 😄.
То же самое верно и для случая асинхронного промежуточного программного обеспечения сredux-thunkНапример:
// 这是简化后的 redux-thunk
const thunk = ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
};
Здесь видно, что когдаdispatchполученоactionЕсли это функция, будет предпринята попытка вложить функцию. После применения этого промежуточного программного обеспеченияdispatchСпособ более "умный", вот почемуreduxоговорено вactionдолжен быть чистым объектом вredux-thunkкитайский языкactionно этоfunctionбез сообщения причины ошибки.
резюме
Промежуточное ПО Redux реализовано путем переопределения метода store.dispatch.action -> reducerПерехват промежуточного программного обеспечения redux можно более четко понять из приведенного выше описания.洋葱圈模型:
中间件A -> 中间件B-> 中间件C-> 原始 dispatch -> 中间件C -> 中间件B -> 中间件A
Это также напоминает нам о том, что при использовании промежуточного программного обеспечения нам нужно обращать внимание на то, когда промежуточное программное обеспечение «делает что-то», например:redux-thunkв исполненииnext(action)тип перехватывается доfunctionизaction,а такжеredux-sagaвnext(action)вызовет мониторингsagaEmitter.emit(action), не блокирует существующиеactionприбытьreducer。
Приложение: Реализация функции компоновки
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
Тонкость заключается в умном использованииArray.prototype.reduceRight(callback[, initialValue])Это функция, которую мы обычно не используем. Этот метод вызывает каждый элемент массива справа налево.callback, в этом примереcallbackто есть
(composed, f) => f(composed)
initialValueНачальное значение — это последняя функция в массиве.
Вот еще реализация:
const compose = (...funcs) => (result) => {
//... 省略边界判断
for (var i = funcs.length - 1; i > -1; i--) {
result = funcs[i].call(this, result);
}
return result;
}
Такой способ написания легче понять, почемуcompose(f, g, h)(...args)Эффект эквивалентенf(g(h(...args))), но не так элегантно, как выше.