Автор оригинала: FreeCodeCamp Community Member.
Михаилом Сарджент
Библиотека маршрутизации является ключевым компонентом любого комплекса, одностраничное приложение. Если вы разрабатываете веб-приложения с React и Redux, вы, вероятно, использовали или хотя бы слышали о React Router Отказ Это хорошо известная библиотека маршрутизации для реагирования и отличного решения для многих случаев использования.
Но React Router не является единственным жизнеспособным решением в экосистеме React/Redux. На самом деле, есть тонны решений маршрутизации, построенные для реагирования и для redux Каждый с разными API, функциями и целями – и список растет только. Излишне говорить, что маршрутизация на стороне клиента не уходит в ближайшее время, и все еще есть много места для дизайна в библиотеках маршрутизации завтрашнего дня.
Сегодня я хочу привлечь ваше внимание на предмет маршрутизации в redux. Я представлю и делаю дело для Redux – первая маршрутизация – парадигма, которая делает Redux звезда Из модели маршрутизации и общей нити среди множества решений Redux Routing. Я продемонстрирую, как объединить ядро, рамочную AGNOSTIC API менее 100 строк кода, прежде чем исследует варианты использования реального использования с реагированными и другими фрагментами.
Немного истории
В браузере Расположение (Информация URL) и История сессии (Стопка мест, посещаемых текущей вкладкой браузера), хранятся в глобальном окно
объект. Они доступны через:
window.location
( Расположение API )окно. Хестория
( История API ).
История API предлагает следующее История Методы навигации , Примечательно для их способности обновлять историю браузера и местоположение Не требуя перезагрузки страницы :
pushstate (href)
– толкает новое место на стек историиРелиз (HREF)
– перезаписывает текущее местоположение в стекеназад ()
– навигации к предыдущему расположению в стекевперед ()
– навигации на следующее место на стекеGo (индекс)
– Навигация на местоположение на стеке, в любом направлении.
Вместе API в истории и расположении включают современную парадигму маршрутизации на стороне клиента, известную как Pushstate Routing – первый главный герой нашей истории.
Теперь это почти преступление, чтобы упомянуть истории и аписа местоположения, не упоминание современного Библиотека обертки как История Отказ
Реагировать/история Управление историей сессии с JavaScript github.com.
История
Обеспечивает простую еще мощную API для взаимодействия с историей и местоположением браузера, в то время как охватывающие несоответствия между различными реализациями браузера. Он используется в качестве одноранговой или внутренней зависимости во многих современных библиотеках маршрутизации, и я сделаю несколько ссылок на него по всей этой статье.
Redux и pushstate маршрутизация
Второй главный герой нашей истории – Redux Отказ Это 2017 год, поэтому я избавлю вам введение и справа до такой степени:
Используя простые выталкивающие маршрутизацию в приложении Redux, мы разделяем состояние приложения на двух доменах: история браузера и магазин Redux.
Вот что это выглядит с React Router, который Информации и обертывания История
:
history → React Router ↘ view Redux ↗
Теперь мы знаем, что Не все данные должны проживать в магазине Отказ Например, состояние локального компонента часто является подходящим местом для хранения данных, которые специфичны для одного компонента.
Но данные местоположения не тривиальные. Это динамическая и важная часть состояния приложения – вид данных, которые принадлежат в магазине. Удерживая его в магазине Включает Redux Lucturies, таких как отладку для прохождения времени, а также легкий доступ с любого компонента, подключенного к магазину.
Итак, как мы переместим местоположение в магазин?
Там нет о том, что браузер читает и хранит историю и информацию о местоположении в окно
, но что мы может сделать, это держать Скопировать данных местоположения в магазине и сохраняйте его в синхронизации с браузером.
Разве это не то, что React-Router-Redux делает для React Router?
Да, но только для включения возможностей прохождения времени redux devtools. Приложение по-прежнему зависит от данных о местоположении, проводимом в React Router:
history → React Router ↘ ↕ view Redux ↗
Использование React-Router-Redux
Для чтения данных местоположения из магазина вместо React Router IS обескуражен (Из-за потенциально противоречивых источников истины).
Можем ли мы сделать лучше?
Можем ли мы построить альтернативную модель маршрутизации – один, который построен из земли, чтобы хорошо играть с redux, что позволяет нам читать и обновлять местоположение Способ Redux – с Store.GetState ()
и store.dispatch ()
?
Мы абсолютно можем, и это называется Redux – первая маршрутизация Отказ
Redux-первая маршрутизация
Redux – первая маршрутизация – это вариант на пущевой маршрутизации Это делает Redux звездой модели маршрутизации.
Решение Redux-первое маршрутизация удовлетворяет следующим критериям :
- Расположение проводится в магазине Redux.
- Расположение изменяется путем диспетчеризации действий redux.
- Приложение читает данные о местоположении исключительно из магазина.
- История магазина и браузера сохраняются в синхронизации за кулисами.
Вот основное представление о том, что выглядит так:
history ↕ Redux → router → view
Подождите, нет еще двух источников местоположения данных?
Да, но если мы можем доверять, что история браузера и магазин Redux находятся в синхронизации, мы можем построить наши приложения в только когда-либо читал данные о местонахождении из магазина Отказ Затем, с точки зрения приложения, есть только один источник правды – магазин.
Как мы выполняем маршрутизацию Redux-первым?
Мы можем начать с создания концептуальной модели, объединяя основополагающие элементы маршрутизации клиента и модели жизненного цикла Redux.
Пересмотр модели маршрутизации клиента
Маршрутизация на стороне клиента – это многоступенчатый процесс, который начинается с навигация и заканчивается рендеринг – Маршрутизация Сама только один шаг в этом процессе! Давайте рассмотрим детали:
- Навигация – Все начинается с изменений в месте. Есть 2 типа навигации: внутренний и внешний Отказ Внутренняя навигация достигается из приложения (например, через API истории), в то время как внешнее навигация возникает, когда пользователь взаимодействует с навигационной панелью браузера или входит в приложение с внешнего сайта.
- Отвечая на навигацию – Когда местоположение изменяется, приложение отвечает, передавая новое местоположение на роутер. Старые методы маршрутизации полагались на опрос
window.location
чтобы достичь этого, но в наши дни у нас есть удобные История. Истень полезность. - Маршрутизация – Далее новое расположение сопоставлено с соответствующим контентом страницы. Код, который обрабатывает этот шаг, называется Маршрутизатор , и обычно принимает входной параметр соответствующих маршрутов и страницы, называемых A Конфигурация маршрута.
- Рендеринг – Наконец, контент оказывается на клиенте. Этот шаг может быть, конечно же, обрабатывается в рамках интерфейса/библиотеки, такими как реакция.
Обратите внимание, что библиотеки маршрутизации не должны обрабатывать каждый Часть модели маршрутизации.
Некоторые библиотеки, такие как React Router и Vue Router , сделать – в то время как другие, как Универсальный маршрутизатор , обеспокоены исключительно одним аспектом (как маршрутизация) Таким образом, обеспечивая гибкость в других аспектах:
Пересмотр модели жизненного цикла Redux Data
Redux может похвастаться односторонней моделью передачи данных/жизненного цикла, которая, вероятно, не нуждается в введении – но вот краткий обзор для хорошей меры:
- Действие – Любое изменение в состоянии начинается за счет распределения действий redux (простой объект, содержащий a
тип
и необязательная полезная нагрузка). - Промежуточное программное обеспечение – Действие проходит через цепочку HAMDWARES магазина, где могут быть перехвачены действия, а может быть выполнено дополнительное поведение. Подразделение обычно используются для обработки побочных эффектов в приложениях Redux.
- Редуктор – Действие затем достигает корневого редуктора, что рассчитывает следующее состояние магазина как чистую функцию предыдущего состояния и полученного действия. Коренный редуктор может быть состоит из индивидуальных редукторов, которые каждый обрабатывает ломтик состояния магазина.
- Новое состояние – Магазин сохраняет новое состояние, возвращенное редуктором, и уведомляет его Подписчики Из изменения (в реакции, через Connect ).
- Рендеринг – Наконец, видом на хранилище, подключенный к магазину может повторно рендерировать в соответствии с новым состоянием.
Создание модели маршрутизации Redux-первая
Неидинациональный характер маршрутизации на стороне клиента и модели жизненного цикла Redux хорошо сами поддается объединенной модели, которая удовлетворяет критериям, которые мы выложили для Redux-первого маршрута.
В этой модели маршрутизатор подписан в магазин, навигация осуществляется через действия Redux, и обновления истории браузера обрабатываются пользовательской промежуточной программой. Давайте рассмотрим детали этой модели:
- Внутренняя навигация через Aredux Действия – Вместо того, чтобы использовать API истории напрямую, внутренняя навигация достигается путем отправки одного из 5 Действия навигации Это отражает методы навигации истории.
- Обновление истории браузера через промежуточное программное обеспечение – промежуточное программное обеспечение используется для перехвата действий навигации и обрабатывать побочный эффект обновления истории браузера. Поскольку новое расположение не обязательно или легко известно без предварительной консультации по истории браузера (например, в случае AF
Go
Action), Действия навигации предотвращают достижение редуктора. - Отвечая на навигацию – поток выполнения продолжается с
История
Слушатель, который отвечает на навигацию (как от промежуточного программного обеспечения и внешняя навигация), отправляя Второе действие что делает Содержать новое местоположение. - Редуктор местоположения – Действие, отправленное слушателем, затем достигает редуктора местоположения, что добавляет местоположение в магазин. Редуктор местоположения также определяет Форма состояния местоположения.
- Подключен маршрутизацию – Затем маршрутизатор подключен к магазину может реагистративно определять новую страницу содержимого при уведомлении изменений в месте в магазине.
- Рендеринг – Наконец, страница может быть перезаряжена с новым контентом.
Обратите внимание, что это не Только Способ выполнить Redux-First маршрутизацию – некоторые вариации Особенность использования энханса магазина и/или дополнительной логики в промежуточном программе – но это простая модель, которая охватывает все основания.
Базовая реализация
После модели мы только что посмотрели, давайте реализуем основные API – действия, промежуточное программное обеспечение, слушатель и редуктор.
Мы будем использовать История
Пакет в качестве внутренней зависимости и построить решение постепенно. Если вы предпочитаете следовать с окончательным результатом, вы можете просмотреть его здесь Отказ
Действия
Мы начнем с определения 5 действий навигации, которые отражают методы навигации по истории:
// constants.jsexport const PUSH = 'ROUTER/PUSH';export const REPLACE = 'ROUTER/REPLACE';export const GO = 'ROUTER/GO';export const GO_BACK = 'ROUTER/GO_BACK';export const GO_FORWARD = 'ROUTER/GO_FORWARD';
// actions.jsexport const push = (href) => ({ type: PUSH, payload: href,});
export const replace = (href) => ({ type: REPLACE, payload: href,});
export const go = (index) => ({ type: GO, payload: index,});
export const goBack = () => ({ type: GO_BACK,});
export const goForward = () => ({ type: GO_FORWARD,});
Промежуточное программное обеспечение
Далее давайте определим промежуточное программное обеспечение. Он должен перехватить действия навигации, позвоните соответствующим История
Методы навигации, затем остановите действие от достижения редуктора – но оставьте все другие действия, ненужные:
// middleware.jsexport const routerMiddleware = (history) => () => (next) => (action) => { switch (action.type) { case PUSH: history.push(action.payload); break; case REPLACE: history.replace(action.payload); break; case GO: history.go(action.payload); break; case GO_BACK: history.goBack(); break; case GO_FORWARD: history.goForward(); break; default: return next(action); }};
Если у вас не было возможности написать или осматривать внутренние внутренние промежуточные программы Redux раньше, проверить Это введение Отказ
История слушателя
Далее нам понадобится История
Слушатель, который отвечает на навигацию, отправляя Новый действие, содержащее новое местоположение информации.
Во-первых, давайте добавим новый тип действия и создатель. Интересные части расположения – это Шида
, Поиск
и хеш
– Так вот вот что мы включаем в полезную нагрузку:
// constants.jsexport const LOCATION_CHANGE = 'ROUTER/LOCATION_CHANGE';
// actions.jsexport const locationChange = ({ pathname, search, hash }) => ({ type: LOCATION_CHANGE, payload: { pathname, search, hash, },});
Тогда давайте напишем функцию слушателя:
// listener.jsexport function startListener(history, store) { history.listen((location) => { store.dispatch(locationChange({ pathname: location.pathname, search: location.search, hash: location.hash, })); });}
Мы сделаем одно небольшое дополнение – начальный locationChange
Отправка, учитывать первоначальную запись в приложение (которая не забирается по прослушивалю истории):
// listener.jsexport function startListener(history, store) { store.dispatch(locationChange({ pathname: history.location.pathname, search: history.location.search, hash: history.location.hash, }));
history.listen((location) => { store.dispatch(locationChange({ pathname: location.pathname, search: location.search, hash: location.hash, })); });}
Редуктор
Далее давайте определим редуктор местоположения. Мы будем использовать простую состояние состояния и делать минимальную работу в редукторе:
// reducer.jsconst initialState = { pathname: '/', search: '', hash: '',};
export const routerReducer = (state = initialState, action) => { switch (action.type) { case LOCATION_CHANGE: return { ...state, ...action.payload, }; default: return state; }};
Код приложения
Наконец, давайте подключим наш API в код приложения:
// index.jsimport { combineReducers, applyMiddleware, createStore } from 'redux'import { createBrowserHistory } from 'history'import { routerReducer } from './reducer'import { routerMiddleware } from './middleware'import { startListener } from './listener'import { push } from './actions'
// Create the history objectconst history = createBrowserHistory()
// Build the root reducerconst rootReducer = combineReducers({ // ...otherReducers, router: routerReducer,}) // Build the middlewareconst middleware = routerMiddleware(history)
// Create the storeconst store = createStore(rootReducer, {}, applyMiddleware(middleware))
// Start the history listenerstartListener(history, store)
// Now you can read location data from the store!let currentLocation = store.getState().router.pathname
// You can also subscribe to changes in the location!let unsubscribe = store.subscribe(() => { let previousLocation = currentLocation currentLocation = store.getState().router.pathname
if (previousLocation !== currentLocation) { // You can render your application reactively here! }})
// And you can dispatch navigation actions from anywhere!store.dispatch(push('/about'))
И это все, что есть к этому! Использование API нашей крошечной (под 100 строк кода), мы выполнили все критерии для Redux-первой маршруты:
- Расположение проводится в магазине Redux. ✔
- Расположение изменяется путем диспетчеризации действий redux. ✔
- Приложение читает данные о местоположении исключительно из магазина. ✔
- История магазина и браузера сохраняются в синхронизации за кулисами. ✔
Просмотр всех файлов вместе здесь – Не стесняйтесь импортировать их в свой проект или использовать его в качестве отправной точки для разработки собственной реализации.
Пакет Redux-First-маршрутизации
Я также поставил API вместе в Redux-First-marking пакет, который вы можете NPM установить
и использовать таким же образом.
msarge/redux – первое маршрут Redux-First-Routing – минимальная каркасная агностическая база для выполнения маршрутизации Redux-первой. github.com.
Он включает в себя реализацию, подобную тому, которое мы построили здесь, но с отличным добавлением разборы запросов через Запрос-строка упаковка.
Подождите – как насчет фактического компонента маршрутизации?
Возможно, вы заметили, что Redux-First-marking
обеспокоен только навигационным аспектом модели маршрутизации:
Развязывая навигационное аспект от других аспектов нашей модели маршрутизации, мы получили гибкость – Redux-First-marking
это оба Маршрутизатор-агностический и Framework-agnostic Отказ
Поэтому вы можете соединить его с библиотекой, такой как Универсальный маршрутизатор для создания полного раствора маршрутизации Redux-First для любых интерфейсных структур:
Или вы можете построить убедительные привязки для вашей рамки выбора – и это то, что мы сделаем для реагирования на следующий и последний раздел этой статьи.
Использование с реакцией
Давайте закончим нашу разведку, глядя на то, как мы могли бы создавать соединенные магазинные компоненты для декларативной навигации и маршрутизации в реакции.
Декларативная навигация
Для навигации мы можем использовать магазин
Это просто переопределяет поведение по умолчанию анкерного элемента <
A/> и Dispatc он
Нажмите на нажатие на нажатие:
// Link.jsimport React from 'react';import { connect } from 'react-redux';import { push as pushAction, replace as replaceAction } from './actions';
const Link = (props) => { const { to, replace, children, dispatch, ...other } = props;
const handleClick = (event) => { // Ignore any click other than a left click if ((event.button && event.button !== 0) || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey || event.defaultPrevented === true) { return; } // Prevent the default behaviour (page reload, etc.) event.preventDefault();
// Dispatch the appropriate navigation action if (replace) { dispatch(replaceAction(to)); } else { dispatch(pushAction(to)); } };
return ( {children} );};
export default connect()(Link);
Вы можете найти более полную реализацию здесь Отказ
Декларативный маршрут
Хотя навигационному компоненту мало, существуют бесчисленные способы разработки компонента маршрутизации – что делает его наиболее интересной частью любого решения маршрутизации.
Что является Во всяком случае, роутер?
Как правило, вы можете просматривать маршрутизатор как функцию или черный ящик с двумя входами и одним выходом:
route configuration ↘ matched content current location ↗
Хотя маршрутизация и последующие рендеринга могут возникнуть в отдельных этапах, реагирование позволяет легко и интуитивно соединить их вместе в декларативный маршрутный API. Давайте посмотрим на две стратегии для достижения этого.
Стратегия 1: монолит <Маршрут
R/> COM порицать
Мы можем использовать монолитную, хранить в магазине <Маршрут
R/> Компонент, который:
- принимает объект конфигурации маршрута через реквизиты
- Читает данные о местоположении из магазина Redux
- рассчитывает новый контент всякий раз, когда местоположение изменяется
- Оказывает/переназначивает содержание в зависимости от обстоятельств.
Конфигурация маршрута может быть простой объектом JavaScript, который содержит все соответствующие пути и страницы (A централизованные Конфигурация маршрута).
Вот как это может посмотреть:
const routes = [ { path: '/', page: './pages/Home', }, { path: '/about', page: './pages/About', }, { path: '*', page: './pages/Error', },]
React.render(, document.getElementById('app'))
Довольно просто, верно? Нет необходимости в вложенных маршрутах JSX – просто объект конфигурации одного маршрута, а также один компонент маршрутизатора.
Если эта стратегия обращается к вам, проверьте мою более полную реализацию в Redux-Json-маршрутизатор библиотека. Это обертывает Redux-First-marking
И обеспечивает привязки реагирования для декларативной навигации и маршрутизации, используя стратегии, которые мы осмотрели до сих пор.
msarge/redux-json-marriter Redux-JSON-маршрутизатор – декларативный, Redux – первая маршрутизация для приложений React/Redux. github.com.
Стратегия 2: композибе <Маршрут/> компоненты
В то время как монолитный компонент может быть простой Способ достичь декларативной маршруты в реакции, это определенно не Только способ.
Совместная природа реагирования позволяет другим интересным возможностям: с использованием JSX для определения маршрутов в децентрализован манера. Конечно, главный пример является React Router’s
React.render(<Route path='/about component={About}/> ...
Если это то, что вы заинтересованы в создании или использовании, дайте мне знать в комментариях! Чтобы узнать больше о различных стратегиях конфигурации маршрута, проверьте это Введение на веб-сайте React Router.
Вывод
Я надеюсь, что это исследование помогло углубиться своим знаниям о маршрутизации на стороне клиента и показал, насколько просто это просто выполнить свой путь Redux.
Если вы ищете полный раствор маршрутизации Redux, вы можете использовать Redux-First-marking Пакет с совместимым маршрутизатором, перечисленным в README. И если вы обнаружите, что нуждаетесь в разработке индивидуального решения, надеюсь, этот пост дал вам хорошую отправной точкой для этого.
Если вы хотите узнать больше о маршрутизации на стороне клиента в React и Redux, проверьте следующие статьи – они важны, помогая мне лучше понять темы, которые я покрыл здесь:
- Пусть URL сделает разговор Тайлер Томпсон
- Возможно, вам не понадобится React Router Константин Таркус
- Нужно ли мне нужна библиотека маршрутизации? Джеймсом К. Нельсон
- и бесчисленные информативные дискуссии в React-Router-Redux вопросы.
Маршрутизация на стороне клиента – это пространство с бесконечными возможностями дизайна, и я уверен, что некоторые из вас играли с идеями, похожими на те, которые я поделился здесь. Если вы хотите продолжить разговор, я буду рад подключиться с вами в комментариях или через Twitter Отказ Спасибо за чтение!
Редактировать 22/06/17: также проверьте Эта статья на Redux-First-маршрутизатор , отдельный проект, который использует интеллектуальные типы действий для достижения мощных возможностей маршрутизации.