предисловие
За последние несколько дней я прочесал промежуточное ПО Redux и прочитал документы Redux-Saga, а также исходный код Redux-Thunk и Redux-обещание, В сочетании с некоторыми мыслями об исходном коде Redux я прочитал некоторое время назад, я чувствую, что промежуточное программное обеспечение редукции у меня есть более глубокое понимание частей, поэтому я подытожу его.
1. Механизм промежуточного программного обеспечения Redux
Redux сам по себе предоставляет очень мощную функцию управления потоком данных, но это не единственная его сила, он также позволяет использовать промежуточное программное обеспечение для расширения собственных функций для удовлетворения потребностей пользователей в разработке. Сначала давайте посмотрим на определение промежуточного программного обеспечения:
It provides a third-party extension point between dispatching an action, and the moment it reaches the reducer.
Это описание промежуточного ПО Дэна Абрамова. Короче говоря, промежуточное ПО Redux предоставляет возможность классифицировать действия. В промежуточном программном обеспечении мы можем изменить действие, просматривая каждое действие, которое проходит через него, и выбирая определенный тип действия для соответствующего действия. Это может звучать немного абстрактно. Давайте посмотрим на диаграмму напрямую. Это поток данных redux без промежуточного программного обеспечения:
Выше приведен типичный процесс потока данных с редукцией, но после добавления промежуточного ПО мы можем перехватить и изменить действие по пути. И из-за разнообразия бизнес-сценариев простое изменение диспетчеризации и сокращения, очевидно, не может удовлетворить потребности каждого, поэтому концепция промежуточного программного обеспечения redux представляет собой подключаемый механизм, который можно свободно комбинировать, подключать и отключать. Именно благодаря этому механизму, когда мы используем промежуточное ПО, мы можем удовлетворять ежедневные потребности в разработке, подключая различные промежуточные ПО Каждое промежуточное ПО может обрабатывать относительно независимые бизнес-требования и последовательно соединяться друг с другом:
Как показано на рисунке выше, объект действия, отправленный в Redux Store, будет обрабатываться по очереди несколькими промежуточными программами в Store.Если процесс передачи действия и текущего состояния редьюсеру для обработки рассматривается как промежуточное программное обеспечение, которое существует по умолчанию, то вся обработка действий может состоять из промежуточного ПО. Стоит отметить, что эти мидлвары будут обрабатывать поступающие действия в указанном порядке. Только после того, как мидлвары во фронте выполнят задачу, последние мидлвары будут иметь возможность продолжить обработку действия. Аналогично, у каждого мидлвары есть свои " fuse", когда он считает, что это действие не должно обрабатываться следующим промежуточным программным обеспечением, последнее промежуточное программное обеспечение больше не может обрабатывать это действие.
Причина, по которой различное промежуточное программное обеспечение может использоваться в комбинации, заключается в том, что Redux требует, чтобы все промежуточное программное обеспечение обеспечивало унифицированный интерфейс.Хотя логика каждого промежуточного программного обеспечения отличается, до тех пор, пока соблюдается единый интерфейс, он может быть совместим с Redux и другим промежуточным программным обеспечением. диалоговое окно промежуточного программного обеспечения.
2. Понять механизм средней цены
Поскольку redux предоставляет метод applyMiddleware для загрузки промежуточного программного обеспечения, мы можем сначала взглянуть на исходный код applyMiddleware в redux:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// 利用传入的createStore和reducer和创建一个store
const store = createStore(...args)
let dispatch = () => {
throw new Error(
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
Мы видим, что исходный код applyMiddleware очень прост, но очень захватывающий Конкретную интерпретацию см. в этой моей статье:Интерпретация исходного кода редукции
Из вышеприведенного кода нетрудно увидеть, что ядро функции applyMiddleware заключается в комбинации компоновки, путем наложения различных промежуточных программ слой за слоем на нативную диспетчеризацию, а затем принятия метода карри для проектирования промежуточного программного обеспечения, поэтому что на основе компоновки можно динамически генерировать следующий метод и поддерживать согласованность хранилища.
Может показаться немного запутанным, но давайте посмотрим, как реализуется промежуточное ПО, которое ничего не делает:
const doNothingMidddleware = (dispatch, getState) => next => action => next(action)
Вышеупомянутая функция принимает объект в качестве параметра.В параметре объекта есть два поля: диспетчеризация и getState, которые представляют две одноименные функции в Redux Store, но следует отметить, что не все промежуточное ПО будет использовать их. две функции. Затем функция, возвращаемая doNothingMidddleware, принимает параметр типа next.Этот next является функцией.Если она вызывается, это означает, что промежуточное ПО выполнило свою функцию и передаст управление действием следующему промежуточному ПО. Но следует отметить, что эта функция является не функцией, которая обрабатывает объект действия, а функцией, которую она возвращает и которая принимает действие в качестве параметра. Наконец, функция, принимающая действие в качестве параметра, обрабатывает входящий объект действия, где могут выполняться такие операции, как:
- Вызвать отправку для отправки нового объекта действия
- Вызовите getState, чтобы получить текущее состояние в магазине Redux.
- Вызовите next, чтобы сообщить Redux, что текущее промежуточное ПО завершено, и позволить Redux вызвать следующее промежуточное ПО.
- получить доступ ко всем данным о действии объекта действия
Имея вышеперечисленные функции, промежуточного программного обеспечения достаточно для получения всей информации в Магазине, а также оно имеет достаточную мощность для передачи доступных данных. Прочитав приведенное выше простейшее промежуточное ПО, давайте взглянем на реализацию самого известного промежуточного ПО redux-thunk в redux middleware:
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;
Код redux-thunk очень прост, он разработан с идеей, что функции становятся, он делает функцию каждой функции как можно меньше, а затем реализует сложные функции через вложенную комбинацию функций, как я писал выше. то же самое касается самого простого промежуточного программного обеспечения (конечно, это промежуточное программное обеспечение для кожи дыни). Функция промежуточного программного обеспечения redux-thunk также очень проста. Сначала проверьте тип действия параметра, если это функция, выполните функцию действия и передайте диспетчеризацию, getState, extraArgument в качестве параметров, в противном случае вызовите next, чтобы позволить следующему промежуточному программному обеспечению продолжить обработку действия.
Следует отметить, что возвращаемое значение функции, обрабатывающей параметр действия на самом внутреннем уровне каждого промежуточного ПО, повлияет на возвращаемое значение функции отправки в Store, но возвращаемое значение этой функции в каждом промежуточном ПО может быть разным. Например, промежуточное программное обеспечение react-thunk, описанное выше, может возвращать функцию действия или результат, возвращаемый следующим промежуточным программным обеспечением. Поэтому возвращаемый результат вызова функции диспетчеризации обычно не поддается контролю, и нам лучше не полагаться на возвращаемое значение функции диспетчеризации.
3. Асинхронный поток избыточности
Среди множества промежуточного программного обеспечения ключевую роль играет промежуточное программное обеспечение, которое обрабатывает избыточные асинхронные события. От простой реакции-перехода до редукс-обещания, редукс-саги и т. д. — все они представляют соответствующие решения проблемы управления асинхронным потоком с редукцией.
3.1 redux-thunk
Ранее мы обсуждали redux-thunk, который реализует ленивую оценку функций посредством многопараметрического каррирования, тем самым превращая синхронные действия в асинхронные. После понимания redux-thunk, когда мы реализуем запрос данных, действие можно записать так:
function getWeather(url, params) {
return (dispatch, getState) => {
fetch(url, params)
.then(result => {
dispatch({
type: 'GET_WEATHER_SUCCESS', payload: result,
});
})
.catch(err => {
dispatch({
type: 'GET_WEATHER_ERROR', error: err,
});
});
};
}
Хотя redux-thunk очень прост и практичен, люди всегда ищут, и все они ищут более элегантный метод реализации управления асинхронным потоком redux, который называется redux-promise.
3.2 redux-promise
Различное промежуточное ПО имеет свои собственные применимые сценарии. React-thunk больше подходит для простых сценариев запроса API, а Promise больше подходит для операций ввода и вывода. Результат, возвращаемый при сравнении функции fetch, представляет собой объект Promise. Посмотрите, как работает простейший объект Promise. реализовано:
import { isFSA } from 'flux-standard-action';
function isPromise(val) {
return val && typeof val.then === 'function';
}
export default function promiseMiddleware({ dispatch }) {
return next => action => {
if (!isFSA(action)) {
return isPromise(action)
? action.then(dispatch)
: next(action);
}
return isPromise(action.payload)
? action.payload.then(
result => dispatch({ ...action, payload: result }),
error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
}
)
: next(action);
};
}
Его логика также очень проста, в основном в следующих двух частях:
- Сначала оцените, является ли это стандартным действием потока. Если нет, то оцените, является ли это обещанием, если да, выполните action.then(dispatch), в противном случае выполните next(action).
- Если это так, сначала определите, является ли полезная нагрузка обещанием, если да, то payload.then получает данные, а затем использует данные в качестве полезной нагрузки для повторной отправки({ ...action, payload: result}); если нет , выполнить следующее (действие)
В сочетании с redux-promise мы можем использовать синтаксис async и await es7 для упрощения асинхронных операций, например:
const fetchData = (url, params) => fetch(url, params)
async function getWeather(url, params) {
const result = await fetchData(url, params)
if (result.error) {
return {
type: 'GET_WEATHER_ERROR', error: result.error,
}
}
return {
type: 'GET_WEATHER_SUCCESS', payload: result,
}
}
3.3 redux-saga
redux-saga — это промежуточное ПО для управления асинхронными операциями приложений redux, заменяющее redux-thunk. Он отделяет синхронные операции от асинхронных операций в реакции, создавая Sagas для хранения всей логики асинхронных операций в одном месте для централизованной обработки, чтобы облегчить последующее управление и обслуживание. Для Saga мы можем просто определить это следующим образом:
Saga = Worker + Watcher
Redux-saga эквивалентна добавлению слоя к исходному потоку данных Redux.Путем мониторинга действия можно захватить отслеживаемое действие, а затем создать новую задачу для поддержания состояния (это зависит от потребностей проекта себя), изменения в представлении управляются измененным состоянием. Как показано ниже:
особенности саги:
- Сценарий приложения саги сложный асинхронный.
- Вы можете использовать takeEvery, чтобы распечатать регистратор (регистратор — хороший способ) для легкого тестирования.
- Предоставьте метод takeLatest/takeEvery/throttle, который может облегчить реализацию того, следует ли сосредоточиться на недавней практике или ограничении времени каждой практики.
- Предоставление методам отмена / задержки может быть удобно быть отменено или задерживается асинхронный запрос.
- Предоставляет методы гонки(эффектов), [...эффектов] для поддержки сценариев гонки и параллельных сценариев.
- Предоставляет механизм канала для поддержки внешних событий.
function *getCurrCity(ip) {
const data = yield call('/api/getCurrCity.json', { ip })
yield put({
type: 'GET_CITY_SUCCESS', payload: data,
})
}
function * getWeather(cityId) {
const data = yield call('/api/getWeatherInfo.json', { cityId })
yield put({
type: 'GET_WEATHER_SUCCESS', payload: data,
})
}
function loadInitData(ip) {
yield getCurrCity(ip)
yield getWeather(getCityIdWithState(state))
yield put({
type: 'GET_DATA_SUCCESS',
})
}
В целом Redux Saga подходит для сценариев с детализированными требованиями к операциям с событиями, в то же время обеспечивает лучшую тестируемость и ремонтопригодность, больше подходит для масштабных проектов с высокими требованиями к асинхронной обработке, в то время как небольшие и простой Проект может полностью использовать redux-thunk для удовлетворения собственных нужд. В конце концов, react-thunk не является инвазивным для самого проекта и чрезвычайно прост в использовании, достаточно ввести это промежуточное ПО. С другой стороны, react-saga требовательнее и сложнее, но и элегантнее (хотя я думаю, что asycn await элегантнее). Я не осваивал и не практиковал этот метод управления асинхронным потоком, поэтому я не буду сначала обсуждать вещи более низкого уровня.
Использованная литература:
- «Введение в React и Redux»
- «Погрузитесь в стек React»