Иллюстрация луковой модели промежуточного ПО в Redux

внешний интерфейс GitHub React.js Redux
Иллюстрация луковой модели промежуточного ПО в Redux

Первоначально опубликовано на моемБлог GitHub, добро пожаловать звезда 😳

предисловие

Недавно я откопал ранее проанализированный applyMiddleware и обнаружил, что снова не могу его понять😳, я перечитал исходный код, разобрался в методе реализации onion-модели и поделился им здесь.

apply Анализ исходного кода ПО промежуточного слоя

Функция applyMiddleware — самая короткая, но наиболее существенная часть Redux, которая успешно позволяет Redux иметь большое пространство для расширения и приносит бесчисленные «побочные эффекты» в процессе выполнения действий, хотя это часто является источником проблем. Идея луковой модели этого промежуточного программного обеспечения взята из промежуточного программного обеспечения koa, и наиболее интуитивно понятно использовать диаграмму для ее представления.

Перед картинкой выше предыдущий код для примера (черезЛуковая модель промежуточного ПО), мы поймем механизм onion модели applyMiddleware вокруг этого кода:

function M1(store) {
  return function(next) {
    return function(action) {
      console.log('A middleware1 开始');
      next(action)
      console.log('B middleware1 结束');
    };
  };
}

function M2(store) {
  return function(next) {
    return function(action) {
      console.log('C middleware2 开始');
      next(action)
      console.log('D middleware2 结束');
    };
  };
}

function M3(store) {
  return function(next) {
    return function(action) {
      console.log('E middleware3 开始');
      next(action)
      console.log('F middleware3 结束');
    };
  };
}
  
function reducer(state, action) {
  if (action.type === 'MIDDLEWARE_TEST') {
    console.log('======= G =======');  
  }
  return {};
}
  
var store = Redux.createStore(
  reducer,
  Redux.applyMiddleware(
    M1,
    M2,
    M3
  )
);

store.dispatch({ type: 'MIDDLEWARE_TEST' });

Затем поместите схематическую диаграмму луковой модели Redux (черезЛуковая модель промежуточного ПО), луковая модель вышеприведенного кода промежуточного слоя выглядит следующим образом:

            --------------------------------------
            |            middleware1              |
            |    ----------------------------     |
            |    |       middleware2         |    |
            |    |    -------------------    |    |
            |    |    |  middleware3    |    |    |
            |    |    |                 |    |    |
          next next next  ———————————   |    |    |
dispatch  —————————————> |  reducer  | — 收尾工作->|
nextState <————————————— |     G     |  |    |    |
            | A  | C  | E ——————————— F |  D |  B |
            |    |    |                 |    |    |
            |    |    -------------------    |    |
            |    ----------------------------     |
            --------------------------------------


顺序 A -> C -> E -> G -> F -> D -> B
    \---------------/   \----------/
            ↓                ↓
      更新 state 完毕      收尾工作

Мы называем ту часть каждого промежуточного программного обеспечения, которая действительно приносит побочные эффекты (побочные эффекты здесь хороши, все, что нам нужно, это побочные эффекты промежуточного программного обеспечения), называемой побочными эффектами M?, а ее сигнатура функции(action) => {}(помнить имя).

image

Для этого примера кода луковая модель промежуточного программного обеспечения Redux работает следующим образом:

Пользователь отправляет действие → действие переходит в побочный эффект M1 → печатает A → выполняет следующее из M1 (этот следующий указывает на побочный эффект M2) → печатает C → выполняет следующий из M2 (этот следующий указывает на побочный эффект M3) → печатает E → выполняет следующее из M3 (это следующее направлениеstore.dispatch) → вернуться к M3, вывести F → ​​вернуться к M2, вывести E → вернуться к M1, вывести B для побочных эффектов -> отправка завершена.

Итак, вопрос в том, как связан следующий из M1 M2 M3?

Ответ: привязка Curried, полная сигнатура функции промежуточного программного обеспеченияstore => next => action {}, но в onion-модели остается только действие, выполняемое в конце. Внешнее хранилище и next привязываются к соответствующим функциям через каррирование. Далее посмотрим, как привязывается next.

const store = createStore(...args)
let chain = []
const middlewareAPI = {
    getState: store.getState,
    dispatch: (...args) => dispatch(...args)
}
chain = middlewares.map(middleware => middleware(middlewareAPI)) // 绑定 {dispatch和getState}
dispatch = compose(...chain)(store.dispatch) // 绑定 next

Ключевым моментом является связывание двух предложений.Давайте сначала посмотрим на первое предложение.

chain = middlewares.map(middleware => middleware(middlewareAPI)) // 绑定 {dispatch和getState}

зачем связыватьgetState? ПО промежуточного слоя должно получать текущее состояние в любое время, зачем его получать?dispatch? Поскольку могут быть варианты поведения, которые отправляют действия в промежуточном программном обеспечении (например, redux-thunk), используйте эту функцию сопоставления для каррирования привязки.getStateа такжеdispatch.

В настоящее времяchain = [(next)=>(action)=>{…}, (next)=>(action)=>{…}, (next)=>(action)=>{…}],Ссылки на закрытиеdispatchа такжеgetState.

следующийdispatch = compose(...chain)(store.dispatch), сначала поймиcomposeфункция

compose(A, B, C)(arg) === A(B(C(arg)))

Это роль компоновки.Справа налево возвращаемое значение справа передается как параметр слева, и слои оборачиваются.Вложенный декоратор в React записывается так, например:

compose(D1, D2, D3)(Button)
// 层层包裹后的组件就是
<D1>
    <D2>
        <D3>
        	<Button />
        </D3>
    </D2>
</D1>

Назад к Редукс

dispatch = compose(...chain)(store.dispatch) 

в примере код эквивалентен

dispatch = MC1(MC2(MC3(store.dispatch)))

MC — это элемент цепочки, да, это снова каррирование.

image

На данный момент правда раскрыта, диспетчер внес небольшой вклад и в общей сложности сделал две вещи: 1. Привязал следующее из каждого промежуточного программного обеспечения. 2. Откройте интерфейс для получения действий. На самом деле, так много было сказано,промежуточное ПО настраивает отправку, эта рассылка будет передаваться по модели onion.

Хорошо, теперь у нас есть желанная диспетчеризация, и мы можем закрыть ее, когда вернемся. Давайте посмотрим на поток души финальной казни:

wx20180424-001706 2x

деталь

Однако Да Дак нахмурился и обнаружил, что все не так просто, есть несколько вопросов для размышления.

dispatch

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }

Анонимная функция, используемая здесь для отправки, должна иметь возможность вызывать последнюю отправку (закрытие) компоновки в промежуточном программном обеспечении, которая должна быть анонимной функцией, а не напрямую писать store.dispatch.

Если прямо написать какstore.dispatch, то в промежуточном ПО (кроме последнего, последнее промежуточное ПО получает исходныйstore.dispatch) отправить действие, например, redux-thunk

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

Он заключается в том, чтобы перехватить действие типа функции, а затем выполнить его снова, чтобы выставить API действия в виде функции (фактически actionCreator).Если actionCreator является вложенной многоуровневой функцией, actionCreator после выполнения actionCreator должен иметь возможность ссылаться на последний actionCreator. Если она не написана как анонимная функция, то actionCreator остался без промежуточного оформления.store.dispatch, что явно не работает. Поэтому напишите это как ссылку на закрытие анонимной функции.

Также здесь используется...argsвместоaction, потому что естьPR, автор этого PR считает, что при диспетчеризации необходимо указать несколько параметров, напримерdispatch(action, option), такая ситуация существует, но только когда промежуточное ПО, которое должно предоставить несколько параметров, вызывается первым промежуточным ПО (т. е. последним в массиве промежуточных ПО).утверждатьЭффективно, потому что нет гарантии, что последнее ПО промежуточного слоя, вызвавшее это ПО промежуточного слоя с несколькими параметрами, было вызвано с использованием next(action) или next(...args), поэтому в этом PR оно было изменено на next(...args). В обсуждении мы видим, что у Дэна есть оговорки по поводу этого изменения (но он все равно его изменил). Это изменение на самом деле довольно болезненно. Как чисто стороннее промежуточное ПО, как я могу знать, что ваше последнее промежуточное ПО передано. беспорядочных атрибутов, и я не знаю, что это значит, когда я передаю это, бро. Я чувствую, что это для того, чтобы какой-то мидлвар использовался вместе.Я не хочу что-то добавлять к действию, поэтому добавляю в параметры.Только эти мидлвары с согласованными параметрами могут знать, что это за параметры.

redux-logger

Note: logger must be the last middleware in chain, otherwise it will log thunk and promise, not actual actions (#20).

Требуется, чтобы вы поставили себя в последнее промежуточное программное обеспечение, причина в том, что

Otherwise it'll log thunks and promises but not actual actions.

Только подумайте, что хочет регистрировать логгер? то естьstore.dispatchинформация о времени, поэтому регистратор должен быть вstore.dispatchДо и после консоли вы еще помните какой мидлвар выше получил store.dispatch, который последним.loggerПоставь это первым, и ты сможешь играть во всеaction, подобноredux-thunkДля actionCreator количество отпечатков должно быть больше последнего, потому что не все действия могут идти до конца, а новые действия диспетчеризуются в middleware.

Ссылаться на

Подробное объяснение промежуточного программного обеспечения redux

Расширенное руководство по Redux

Анализ принципа redux applyMiddleware

рисунок

ProcessOn