Промежуточное ПО 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)))
, но не так элегантно, как выше.