Michael Sargent

směrovací knihovna je klíčovou součástí jakékoli složité jednostránkové aplikace. Pokud vyvíjíte webové aplikace s React a Redux, pravděpodobně jste použili nebo alespoň slyšeli o React routeru. Je to známá směrovací Knihovna pro React a skvělé řešení pro mnoho případů použití.

ale React Router není jediným životaschopným řešením v ekosystému React / Redux. Ve skutečnosti existuje spousta směrovacích řešení postavených pro React a pro Redux, každé s různými API, funkcemi a cíli — a seznam se jen rozrůstá. Netřeba dodávat, že směrování na straně klienta v dohledné době nezmizí a ve směrovacích knihovnách zítřka je stále mnoho prostoru pro návrh.

dnes chci upozornit na téma směrování v Reduxu. Představím a udělám případ pro Redux-první směrování-paradigma, díky kterému je Redux hvězdou směrovacího modelu a společným vláknem mezi mnoha směrovacími řešeními Redux. Ukážu, jak dát dohromady jádro, framework-agnostic API v rámci 100 řádků kódu, než prozkoumám možnosti použití v reálném světě s React a dalšími front-end frameworky.

trochu historie

v prohlížeči jsou umístění (informace o adrese URL) a historie relací (hromada míst navštívených aktuální kartou prohlížeče) uloženy v globálním objektu window. Jsou přístupné přes:

  • window.location (umístění API)
  • window.history (historie API).

rozhraní History API nabízí následující metody navigace v historii, které se vyznačují schopností aktualizovat historii a umístění prohlížeče bez nutnosti opětovného načtení stránky:

  • pushState(href) — posune nové umístění do zásobníku historie
  • replaceState(href) — přepíše aktuální umístění na zásobníku
  • back() — přejde na předchozí umístění na zásobníku
  • forward() — přejde na další místo na zásobníku
  • go(index) — naviguje na místo na zásobníku, v obou směrech.

společně API historie a umístění umožňují moderní směrovací paradigma na straně klienta známé jako pushState routing-první protagonista našeho příběhu.

nyní je téměř zločin zmínit historii a umístění API, aniž bychom zmínili moderní knihovnu obalů, jako je history.

ReactTraining / history
Správa Historie relací pomocí JavaScriptgithub.com

history poskytuje jednoduché, ale výkonné rozhraní API pro propojení s historií a umístěním prohlížeče a zároveň pokrývá nesrovnalosti mezi různými implementacemi prohlížeče. Používá se jako peer nebo interní závislost v mnoha moderních směrovacích knihovnách a v tomto článku na ni uvedu několik odkazů.

Redux a pushState směrování

druhým protagonistou našeho příběhu je Redux. Je to 2017, takže vám ušetřím úvod a dostanu se přímo k věci:

pomocí prostého směrování pushState v aplikaci Redux rozdělíme stav aplikace na dvě domény: historii prohlížeče a obchod Redux.

zde je to, co to vypadá s React Router, který instance a zábaly history:

history → React Router ↘ view Redux ↗

nyní víme, že ne všechna data musí být umístěna v obchodě. Například stav místní komponenty je často vhodným místem pro ukládání dat, která jsou specifická pro jednu komponentu.

ale údaje o poloze nejsou triviální. Je to dynamická a důležitá součást stavu aplikace — druh dat, která patří do obchodu. Držení v obchodě umožňuje Redux luxus, jako je ladění cestování v čase, a snadný přístup z jakékoli komponenty připojené k obchodu.

jak tedy přesuneme místo do obchodu?

není možné obejít skutečnost, že prohlížeč čte a ukládá informace o historii a poloze v window, ale co můžeme udělat, je ponechat kopii údajů o poloze v obchodě a udržovat je v synchronizaci s prohlížečem.

není to, co react-router-redux dělá pro React Router?

Ano, ale pouze pro povolení možností cestování v čase Redux DevTools. Aplikace stále závisí na lokalizačních datech uchovávaných v React routeru:

history → React Router ↘ ↕ view Redux ↗

použití react-router-redux ke čtení dat o poloze z úložiště namísto React routeru se nedoporučuje (kvůli potenciálně konfliktním zdrojům pravdy).

můžeme dělat lépe?

můžeme vytvořit alternativní směrovací model — ten, který je postaven od základu, aby se dobře hrál s Reduxem, což nám umožňuje číst a aktualizovat umístění Redux způsobem-s store.getState() a store.dispatch()?

absolutně můžeme a nazývá se Redux-first routing.

Redux – první směrování

Redux-první směrování je variace na směrování pushState, díky kterému je Redux hvězdou směrovacího modelu.

směrovací řešení Redux-first splňuje následující kritéria:

  • místo se koná v obchodě Redux.
  • umístění se změní odesláním akcí Redux.
  • aplikace čte údaje o poloze výhradně z obchodu.
  • historie obchodu a prohlížeče jsou synchronizovány v zákulisí.

zde je základní představa o tom, jak to vypadá:

history ↕ Redux → router → view

počkejte, nejsou ještě dva zdroje lokalizačních dat?

Ano, ale pokud můžeme věřit, že historie prohlížeče a úložiště Redux jsou synchronizovány, můžeme vytvářet naše aplikace tak, aby z obchodu četly pouze data o poloze. Z pohledu aplikace je pak jediný zdroj pravdy-obchod.

jak dosáhneme Redux-first routing?

můžeme začít vytvořením koncepčního modelu sloučením základních prvků směrovacích modelů na straně klienta a modelů životního cyklu dat Redux.

revize modelu směrování na straně klienta

směrování na straně klienta je vícestupňový proces, který začíná navigací a končí vykreslováním-samotné směrování je pouze jedním krokem v tomto procesu! Podívejme se na podrobnosti:

  • navigace-Vše začíná změnou umístění. Existují 2 typy navigace: interní a externí. Interní navigace se provádí přímo z aplikace (např. k externí navigaci dochází, když uživatel interaguje s navigačním panelem prohlížeče nebo vstoupí do aplikace z externího webu.
  • reakce na navigaci – když se změní umístění, aplikace odpoví předáním nového umístění routeru. Starší směrovací techniky se spoléhaly na dotazování window.location, ale dnes máme šikovný nástroj history.listen.
  • směrování-dále je nové umístění přizpůsobeno odpovídajícímu obsahu stránky. Kód, který zpracovává tento krok se nazývá router, a to obecně trvá vstupní parametr odpovídající trasy a stránky zvané konfigurace trasy.
  • Vykreslování-nakonec je obsah vykreslen na klienta. Tento krok může být samozřejmě zpracován front-end frameworkem / knihovnou, jako je React.

Všimněte si, že směrovací knihovny nemusí zpracovávat každou část směrovacího modelu.

některé knihovny, jako React Router a Vue Router, do — zatímco jiné, jako Universal Router, se zabývají pouze jedním aspektem (jako je směrování), čímž poskytují flexibilitu v ostatních aspektech:

směrovací knihovny mohou mít různé oblasti odpovědnosti. (Klikněte pro zvětšení)

revize modelu životního cyklu dat Redux

Redux se může pochlubit jednosměrným modelem toku dat/životního cyklu, který pravděpodobně nepotřebuje úvod — ale zde je stručný přehled pro správnou míru:

  • akce-jakákoli změna stavu začíná odesláním akce Redux (prostý objekt obsahující type a volitelné užitečné zatížení).
  • Middleware-akce prochází řetězcem middlewarů v obchodě, kde mohou být akce zachyceny a může být provedeno další chování. Middlewares se běžně používají ke zpracování vedlejších účinků v aplikacích Redux.
  • reduktor — akce pak dosáhne kořenového reduktoru, který vypočítá další stav obchodu jako čistou funkci předchozího stavu a přijaté akce. Kořenový reduktor může být složen z jednotlivých reduktorů, z nichž každý zpracovává plátek stavu obchodu.
  • nový stav-obchod uloží nový stav vrácený reduktorem a upozorní své předplatitele na změnu (v React, přes connect).
  • Rendering-nakonec se zobrazení připojené k úložišti může znovu vykreslit v souladu s novým stavem.

budování Redux-první směrovací Model

jednosměrná povaha směrování na straně klienta a modelů životního cyklu dat Redux se hodí ke sloučenému modelu, který splňuje kritéria, která jsme stanovili pro směrování Redux-first.

v tomto modelu je router přihlášen k odběru obchodu, navigace je prováděna pomocí akcí Redux a aktualizace historie prohlížeče jsou zpracovávány vlastním middlewarem. Podívejme se na podrobnosti tohoto modelu:

  • interní navigace prostřednictvím akcí Redux – namísto přímého použití API historie je interní navigace dosažena odesláním jedné z 5 navigačních akcí, které zrcadlí metody navigace historie.
  • aktualizace historie prohlížeče prostřednictvím middleware-middleware se používá k zachycení navigačních akcí a zpracování vedlejšího efektu aktualizace historie prohlížeče. Vzhledem k tomu, že nové umístění není nutně nebo snadno známé bez předchozí konzultace s historií prohlížeče (např. v případě akce go) je zabráněno navigačním akcím v dosažení reduktoru.
  • reakce na navigaci-tok provádění pokračuje posluchačem history, který reaguje na navigaci (z middleware i externí navigace) odesláním druhé akce, která obsahuje nové umístění.
  • reduktor polohy – akce odeslaná posluchačem pak dosáhne reduktoru polohy, který přidá umístění do obchodu. Reduktor umístění také určuje tvar stavu umístění.
  • Connected routing-router připojený k úložišti pak může reaktivně určit nový obsah stránky, když je upozorněn na změnu umístění v obchodě.
  • Vykreslování-nakonec může být stránka znovu vykreslena s novým obsahem.

Všimněte si, že to není jediný způsob, jak dosáhnout Redux-první směrování-některé varianty mají použití úložiště enhancer a / nebo další logiku v middleware-ale je to jednoduchý model, který pokrývá všechny základny —

základní implementace

podle modelu, na který jsme se právě podívali, implementujme základní API-akce, middleware, listener a reducer.

použijeme balíček history jako interní závislost a sestavíme řešení postupně. Pokud byste raději sledovali konečný výsledek, můžete si jej prohlédnout zde.

akce

začneme definováním 5 navigačních akcí, které zrcadlí metody navigace historie:

// 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

dále definujme middleware. Měl by zachytit navigační akce, zavolat odpovídající navigační metody history a zastavit akci v dosažení reduktoru – ale všechny ostatní akce ponechat nerušené:

// 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); }};

pokud jste ještě neměli možnost napsat nebo prozkoumat vnitřky Redux middleware dříve, podívejte se na tento úvod.

historie Listener

dále budeme potřebovat history listener, který reaguje na navigaci odesláním nové akce obsahující nové informace o poloze.

nejprve přidáme nový typ akce a tvůrce. Zajímavé části místa jsou pathname, search a hash – to je to, co zahrneme do užitečného zatížení:

// constants.jsexport const LOCATION_CHANGE = 'ROUTER/LOCATION_CHANGE';
// actions.jsexport const locationChange = ({ pathname, search, hash }) => ({ type: LOCATION_CHANGE, payload: { pathname, search, hash, },});

pak napíšeme funkci posluchače:

// listener.jsexport function startListener(history, store) { history.listen((location) => { store.dispatch(locationChange({ pathname: location.pathname, search: location.search, hash: location.hash, })); });}

uděláme jeden malý doplněk-počáteční odeslání locationChange, které bude odpovídat za počáteční vstup do aplikace (který posluchač historie nezachytí):

// 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, })); });}

reduktor

dále definujeme redukci umístění. Použijeme jednoduchý tvar stavu a uděláme minimální práci v reduktoru:

// 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; }};

kód aplikace

nakonec připojme naše API do kódu aplikace:

// 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'))

a to je vše, co je k tomu! Pomocí našeho malého (pod 100 řádků kódu) API jsme splnili všechna kritéria pro Redux-first routing:

  • místo se koná v obchodě Redux. ✔
  • umístění se změní odesláním akcí Redux. ✔
  • aplikace čte údaje o poloze výhradně z obchodu. ✔
  • historie obchodu a prohlížeče jsou synchronizovány v zákulisí. ✔

Zobrazit všechny soubory společně zde-neváhejte je importovat do svého projektu, nebo jej použít jako výchozí bod pro vývoj vlastní implementace.

balíček redux-first-routing

také jsem dal API dohromady do balíčku redux-first-routing, který můžete npm install a používat stejným způsobem.

mksarge / redux-first-routing
redux-first-routing — minimální, framework-agnostická základna pro dosažení Redux-first routing.github.com

obsahuje implementaci podobnou té, kterou jsme zde vytvořili, ale s pozoruhodným přidáním analýzy dotazů prostřednictvím balíčku query-string.

počkejte-a co skutečná komponenta směrování?

možná jste si všimli, že redux-first-routing se týká pouze navigačního aspektu směrovacího modelu:

oddělením navigačního aspektu od ostatních aspektů našeho směrovacího modelu jsme získali určitou flexibilitu – redux-first-routing je jak router-Agnostický, tak framework-Agnostický.

proto jej můžete spárovat s knihovnou, jako je Universal Router, a vytvořit tak kompletní řešení Redux-first routing pro jakýkoli front-end framework:

klikněte sem a začněte s redux-first-routing + universal-router.

nebo, můžete vytvořit umíněné vazby pro váš rámec výběru — a to je to, co uděláme pro React v další a poslední části tohoto článku.

použití s React

pojďme dokončit náš průzkum tím, že se podíváme na to, jak bychom mohli vytvořit komponenty připojené k úložišti pro deklarativní navigaci a směrování v React.

deklarativní navigace

pro navigaci můžeme použít komponentu připojenou k úložišti <Lin k/ > podobnou komponentě v React routeru a dalších řešeních React routing.

jednoduše přepíše výchozí chování kotevního prvku <a / > a dispečerhes push akci po kliknutí:

// 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 ( <a href={to} onClick={handleClick} {...other}> {children} </a>);};
export default connect()(Link);

kompletní implementaci najdete zde.

deklarativní směrování

i když navigační komponenta není moc, existuje nespočet způsobů, jak navrhnout směrovací komponentu — což z ní činí nejzajímavější část jakéhokoli směrovacího řešení.

co je to router?

router můžete obecně zobrazit jako funkci nebo černou skříňku se dvěma vstupy a jedním výstupem:

route configuration ↘ matched content current location ↗

ačkoli směrování a následné Vykreslování může probíhat v samostatných krocích, React usnadňuje a intuitivně je sdružuje do deklarativního směrovacího API. Podívejme se na dvě strategie, jak toho dosáhnout.

strategie 1: monolitická <Router / > komponenta

můžeme použít monolitickou komponentu připojenou k úložišti <Router / >, která:

  • přijímá objekt konfigurace trasy pomocí rekvizit
  • čte údaje o poloze z úložiště Redux
  • vypočítá nový obsah, kdykoli se změní umístění
  • vykreslí/znovu vykreslí obsah podle potřeby.

konfigurace trasy může být prostý objekt JavaScript, který obsahuje všechny odpovídající cesty a stránky(centralizovaná konfigurace trasy).

zde je návod, jak to může vypadat:

const routes = 
React.render( <Provider store={store}> <Router routes={routes}> </Provider>, document.getElementById('app'))

docela jednoduché, že? Není třeba vnořených tras JSX-pouze jeden konfigurační objekt trasy a jedna komponenta routeru.

pokud je tato strategie pro vás lákavá, podívejte se na mou úplnější implementaci v knihovně redux-json-router. Obaluje redux-first-routing a poskytuje vazby reakcí pro deklarativní navigaci a směrování pomocí strategií, které jsme dosud zkoumali.

mksarge / redux-json-router
redux-json-router-deklarativní, Redux – první směrování pro prohlížeč React / Redux applications.github.com

strategie 2: Kompozovatelné <Route / > komponenty

zatímco monolitická složka může být jednoduchým způsobem, jak dosáhnout deklarativního směrování v React, rozhodně to není jediný způsob.

skládatelná povaha Reactu umožňuje další zajímavou možnost: pomocí JSX definovat trasy decentralizovaným způsobem. Samozřejmě, že hlavním příkladem je React Router je <Route / > API:

React.render( <BrowserRouter> <Route path='/' component={Home}/> <Route path='/about component={About}/> ... </BrowserRouter>

tuto myšlenku zkoumají i další směrovací knihovny. I když jsem neměl šanci to udělat, nevidím žádný důvod, proč by podobné API nemohlo být implementováno na balíček redux-first-routing.

namísto spoléhání se na údaje o poloze poskytnuté <BrowserRoute r / >, the &lt;Route/> komponenta could simply připojit k úložišti:

React.render( <Provider store={store}> <Route path='/' component={Home}/> <Route path='/about component={About}/> ... </Provider>

pokud je to něco, co máte zájem o budování nebo používání, dejte mi vědět v komentářích! Chcete-li se dozvědět více o různých strategiích konfigurace trasy, podívejte se na tento úvod na webových stránkách React routeru.

závěr

doufám, že tento průzkum pomohl prohloubit vaše znalosti o směrování na straně klienta a ukázal vám, jak jednoduché je dosáhnout Redux způsobem.

pokud hledáte kompletní řešení Redux routing, můžete použít balíček redux-first-routing s kompatibilním routerem uvedeným v readme. A pokud zjistíte, že potřebujete vyvinout řešení na míru, doufejme, že vám tento příspěvek poskytl dobrý výchozí bod.

pokud se chcete dozvědět více o směrování na straně klienta v Reactu a Reduxu, podívejte se na následující články — pomohly mi lépe porozumět tématům, kterým jsem se zde věnoval:

  • nechte adresu URL mluvit Tylerem Thompsonem
  • možná nebudete potřebovat React Router Konstantin Tarkus
  • potřebuji vůbec směrovací knihovnu? James K. Nelson
  • a nespočet informativních diskusí v otázkách react-router-redux.

směrování na straně klienta je prostor s nekonečnými možnostmi designu a jsem si jistý, že někteří z vás hráli s nápady podobnými těm, které jsem zde sdílel. Pokud byste chtěli pokračovat v konverzaci, rád se s vámi spojím v komentářích nebo prostřednictvím Twitteru. Díky za přečtení!

upravit 22/06/17: Podívejte se také na tento článek o redux-first-router, samostatném projektu, který využívá inteligentní typy akcí k dosažení výkonných funkcí směrování.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.