Первоначально опубликовано в моемgithub
зачем писать эту статью
В свободное время я читал исходный код многих отличных проектов с открытым исходным кодом, таких как react, redux, vuex, vue, babel, ant-design и т. д., но я редко систематически обобщаю, и полученные знания очень ограничен, поэтому я всегда хочу написать статью о полной интерпретации исходного кода.
Вторая причина заключается в том, что в ходе недавнего собеседования было обнаружено, что многие кандидаты имеют очень поверхностное или даже неправильное понимание редукции. Люди, которые действительно понимают идею редукса, очень хороши, не говоря уже о том, чтобы понять его тонкий дизайн.
Отсюда и родилась эта статья.
Что такое РЕДУКС
Прежде чем мы глубоко разберемся с редуксом, давайте сначала посмотрим, что такое редукс и какие проблемы он решает.
Ниже приводится официальное объяснение, данное Redux:
Redux is a predictable state container for JavaScript apps.
Приведенные выше концепции относительно абстрактны, и людям, не знакомым с редуксом, их трудно понять.
Более простое для понимания объяснение (также официальное объяснение сокращения):
redux — это реализация архитектуры потока, вдохновленная Elm.
Сначала популяризируют два названия, Flux и Elm.
flux
Ниже приводится официальное объяснение потока в Facebook:
Application Architecture for Building User Interfaces
более конкретно:
An application architecture for React utilizing a unidirectional data flow.
Flux — это среда управления данными, запущенная с помощью React, и ее основная идея — единый поток данных.
Картинка стоит тысячи слов, давайте поймем поток через картинки
Представление создается с помощью реакции, а реакция управляется данными, поэтому решение проблемы с данными решает проблему представления и управляет данными через архитектуру потока, делая данные предсказуемыми. так Взгляды также становятся предсказуемыми. очень хорошо~
Elm
Elm — это язык для компиляции кода в javaScript, отличающийся высокой производительностью и отсутствием исключений во время выполнения. В Elm также есть реализация виртуального DOM.
Основная концепция Elm заключается в использовании модели для создания приложений, что означает, что модель является ядром приложения. Создание приложения — это создание модели, создание способа обновления модели и построение сопоставления модели с представлением.
Поняв вышеизложенное, вы обнаружите, что задача redux — управлять данными. Поток данных redux можно обозначить следующей диаграммой:
Ядром редукции является одно состояние. Состояние хранится в хранилище избыточности в виде замыкания, которое гарантированно доступно только для чтения. Если вы хотите изменить состояние, вы можете сделать это, только отправив действие, которое по сути является обычным объектом.
Ваше приложение может подписаться на изменения состояния с помощью метода подписки, предоставляемого Redux. Если вы используете избыточность в реагирующем приложении, оно ведет себя так, как будто реакция подписывается на сохранение изменений и повторно отображает представление.
Последний вопрос заключается в том, как обновить представление в соответствии с действием, эта часть связана с бизнесом. Redux использует редюсер для обновления состояния, о введении редуктора я подробно расскажу позже.
Его изысканный дизайн мы будем интерпретировать позже.
Минимальная реализация REDUX
На самом деле написать редукс не сложно. Исходный код редукса составляет всего около 200 строк. Он использует много знаний о функциях высшего порядка, замыканиях и композиции функций. Сделайте код короче и понятнее.
Давайте напишем"redux"
Бар
выполнить
Редукс, который мы хотим реализовать, в основном имеет следующие функции:
- Получить состояние приложения
- отправить действие
- Отслеживание изменений состояния
Давайте посмотрим на API, представленный в магазине Redux.
const store = {
state: {}, // 全局唯一的state,内部变量,通过getState()获取
listeners: [], // listeners,用来诸如视图更新的操作
dispatch: () => {}, // 分发action
subscribe: () => {}, // 用来订阅state变化
getState: () => {}, // 获取state
}
Давайте реализуем createStore, который возвращает объект хранилища, Объектная структура хранилища была написана выше. createStore используется для инициализации хранилища избыточности и является наиболее важным API-интерфейсом избыточности. Давайте реализуем это:
createStore
const createStore = (reducer, initialState) => {
// internal variables
const store = {};
store.state = initialState;
store.listeners = [];
// api-subscribe
store.subscribe = (listener) => {
store.listeners.push(listener);
};
// api-dispatch
store.dispatch = (action) => {
store.state = reducer(store.state, action);
store.listeners.forEach(listener => listener());
};
// api-getState
store.getState = () => store.state;
return store;
};
Вы удивлены тем, что самые основные функции редукса были реализованы с помощью приведенных выше 20 или около того строк кода? Давайте попробуем это ниже.
использовать
Теперь мы можем использовать наш редукс, например"redux"
.
Следующие примеры взяты с официального сайта
Вы можете добавить следующий скрипт в нашу реализацию выше"redux"
, скопируйте его в консоль для выполнения и посмотрите на эффект. Соответствует ли это официальным результатам, данным Redux.
// reducer
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
let store = createStore(counter)
store.subscribe(() =>
console.log(store.getState())
)
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
Видно, что мы выполнили самые основные функции редукса. Если вам нужно обновить представление, просто обновите его в соответствии с подпиской, которую мы предоставили, что объясняет, что redux не используется специально для реакции, и почему существует такая библиотека, как react-redux.
Чтобы облегчить понимание людям на всех этапах, я опускаю реализацию applyMiddleware, но не волнуйтесь, я буду интерпретировать ее в следующих главах, посвященных основной идее redux.
Основная идея REDUX
Основная идея редукса выходит за рамки только что упомянутых. Лично я считаю, что есть две вещи, которые требуют особого внимания. Один редуктор, другой промежуточное ПО
редуктор и уменьшить
Можно сказать, что редуктор является сущностью редукции. Давайте сначала посмотрим на это. редукторпроситьЯвляетсячистая функция.
- Вопрос имеет решающее значение, потому что редукторы не определены в Redux. Это метод, переданный пользователем.
- Чистые функции также имеют решающее значение.Редуктор должен быть чистой функцией, чтобы состояние было предсказуемым (это подтверждает, что Redux является контейнером предсказуемого состояния для приложений JavaScript, о чем я упоминал в начале).
Мы также используем функцию сокращения в нашей повседневной работе, которая является функцией более высокого порядка. Сокращение всегда было очень важной концепцией в компьютерной сфере.
Редуктор и редукция имеют очень похожие имена, это совпадение?
Давайте сначала посмотрим на функцию подписи редуктора:
fucntion reducer(state, action) {
const nextState = {};
// xxx
return nextState;
}
Давайте посмотрим на сигнатуру функции сокращения
[].reduce((state, action) => {
const nextState = {};
// xxx
return nextState;
}, initialState)
Видно, что они почти идентичны. Основное отличие заключается в том, что для сокращения требуется массив, а затем накапливаются изменения. Редуктор не имеет такого массива.
Точнее редуктор накапливает时间
При изменениях уменьшение суммируется空间
меняется на.
Как понять, что редюсер накопительный时间
изменения в
Каждый раз, когда мы вызываем dispatch(action), мы вызываем редьюсер, а затем обновляем store.state возвращаемым значением редьюсера.
Процесс каждой отправки на самом деле является процессом толчка (действия) в пространстве, примерно так:
[action1, action2, action3].reduce((state, action) => {
const nextState = {};
// xxx
return nextState;
}, initialState)
Следовательно, редуктор — это фактически накопление во времени, и это операция, основанная на времени и пространстве.
middlewares
Мы не будем много рассказывать о концепции промежуточного программного обеспечения. Заинтересованные могут посетитьздесьБольше информации.
Эффект промежуточного программного обеспечения может быть достигнут следующим образом:
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
Приведенный выше код будет печатать информацию до и после отправки, чтобы реализовать простейшее промежуточное ПО. Если вы добавите compose, вы сможете выполнять несколько промежуточных программ последовательно.
Например, мы также определяем несколько других подобных промежуточных программ, нам нужно выполнить несколько промежуточных программ в определенном порядке, ниже приведен исходный код redux applyMiddleware:
// 用reduce实现compose,很巧妙。
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
// applyMiddleware 的源码
function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => null;
let chain = [];
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
// 将middlewares组成一个函数
// 也就是说就从前到后依次执行middlewares
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
// 使用
let store = createStore(
todoApp,
// applyMiddleware() tells createStore() how to handle middleware
applyMiddleware(logger, dispatchAndLog)
)
Выше приведен исходный код redux о промежуточном программном обеспечении, который очень лаконичен. Но нужно приложить некоторые усилия, чтобы полностью понять его.
Сначала редукс генерирует оригинальный стор через createStore (не усовершенствованный), а затем, наконец, переписывает диспетчеризацию исходного стора.Между вызовом исходного редуктора вставляем логику промежуточного ПО (цепочка промежуточного ПО будет выполняться последовательно).Код такой: :
function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
// let dispatch = xxxxx;
return {
...store,
dispatch
}
}
}
Затем мы последовательно выполняем промежуточное программное обеспечение, переданное пользователем. Здесь мы используем compose. Compose — очень важная концепция в функциональном программировании. Его функция состоит в объединении нескольких функций в одну функцию, compose(f, g, h)(), окончательный результат, вероятно, будет таким:
function(...args) {
f(g(h(...args)))
}
Итак, цепочка выглядит так:
chain = [
function middleware1(next) {
// 内部可以通过闭包访问到getState和dispath
},
function middleware2(next) {
// 内部可以通过闭包访问到getState和dispath
},
...
]
Надcompose
После концепции , мы обнаружим, что ввод каждого промежуточного ПО является функцией с параметром next, а следующее, к которому обращается первое промежуточное ПО, фактически является отправкой собственного хранилища. Код в качестве доказательства:dispatch = compose(...chain)(store.dispatch)
. Начиная со второго промежуточного слоя, next — это действие => retureValue, возвращаемое предыдущим промежуточным слоем. Вы обнаружили, что эта сигнатура функции является сигнатурой функции диспетчеризации.
output — это функция, параметром которой является действие, Возвращаемая сигнатура функции: action => retureValue Используется в качестве следующего промежуточного программного обеспечения. Таким образом, промежуточное ПО может дополнительно вызывать следующее промежуточное ПО (следующее).
В сообществе есть много промежуточного программного обеспечения для редукции, самый классический редукционный преобразователь, написанный самим Дэном, основной код только两行
, я был действительно потрясен, когда я увидел это в первый раз. Отсюда вы также можете увидеть силу редукции. Основываясь на отличном дизайне redux, в сообществе есть много отличных сторонних средних цен на redux, таких как redux-dev-tool, redux-log, redux-promise и т. д. Я дам анализ перетока, когда буду есть возможность.
Суммировать
В этой статье в основном объясняется, что такое редукс и что он в основном делает. Затем реализовал минимальное сокращение менее чем в 20 строк кода. Наконец, подробно объяснен редуктор основного дизайна и промежуточное ПО Redux.
Есть также некоторые очень классические учебные ресурсы для Redux. Здесь я рекомендую автору redux.getting started with reduxа такжеYou Might Not Need Redux. Изучение этого очень полезно для понимания избыточности и того, как управлять состоянием приложения с помощью избыточности.