Michael Sargent

az útválasztási könyvtár minden összetett, egyoldalas alkalmazás kulcsfontosságú eleme. Ha webes alkalmazásokat fejleszt a React és a Redux segítségével, akkor valószínűleg használta, vagy legalábbis hallott a React routerről. Ez egy jól ismert útválasztási könyvtár a React számára, és nagyszerű megoldás számos felhasználási esetre.

de a React Router nem az egyetlen életképes megoldás a React/Redux ökoszisztémában. Valójában rengeteg routing megoldás létezik a React és a Redux számára, mindegyik különböző API — kkal, funkciókkal és célokkal-és a lista csak növekszik. Mondanom sem kell, hogy az ügyféloldali útválasztás nem fog eltűnni egyhamar, és még mindig sok hely van a tervezéshez a holnap útválasztási könyvtáraiban.

ma szeretném felhívni a figyelmet A Redux routing témájára. Bemutatom a Redux-first routing paradigmát, amely a Redux-ot az útválasztási modell csillagává teszi, és sok Redux routing megoldás közös szálává teszi. Bemutatom, hogyan kell összeállítani a core, framework-agnostic API-t 100 sornyi kód alatt, mielőtt megvizsgálnám a React és más front-end keretrendszerek valós használatának lehetőségeit.

egy kis történelem

a böngészőben a hely (URL-információk) és a munkamenet-előzmények (az aktuális böngésző lap által meglátogatott helyek halmaza) a globális window objektumban tárolódnak. Ezek elérhető:

  • window.location (hely API)
  • window.history (előzmények API).

a History API a következő előzmények navigációs módszereket kínálja, amelyek figyelemre méltóak a böngésző előzményeinek és helyének frissítésére az oldal újratöltése nélkül:

  • pushState(href) — új helyet tol a történelem verembe
  • replaceState(href) — felülírja a verem aktuális helyét
  • back() — a verem előző helyére navigál
  • forward() — navigál a verem következő helyére
  • go(index) — navigál egy helyet a verem, mindkét irányban.

az Előzmények és a helymeghatározó API-k együttesen teszik lehetővé a pushState routing néven ismert modern ügyféloldali útválasztási paradigmát — történetünk első főszereplőjét.

most már szinte bűn megemlíteni a történelem és a hely API-kat anélkül, hogy megemlítenénk egy olyan modern csomagoló könyvtárat, mint a history.

Reactraining / history
munkamenet-Előzmények kezelése JavaScriptgithub.com

history egy egyszerű, mégis hatékony API-t biztosít a böngésző előzményeinek és helyének összekapcsolására, miközben lefedi a különböző böngésző implementációk közötti ellentmondásokat. Sok modern útválasztási könyvtárban társként vagy belső függőségként használják, és ebben a cikkben többször is hivatkozni fogok rá.

Redux és pushState Routing

történetünk második főszereplője Redux. 2017-et írunk, szóval megkíméllek a bevezetéstől, és rögtön a lényegre térek:

a sima pushState routing használatával egy Redux alkalmazásban az alkalmazás állapotát két tartományra osztottuk: a böngésző előzményeire és a Redux store-ra.

így néz ki ez a React routerrel, amely példányosítja és becsomagolja history:

history → React Router ↘ view Redux ↗

most már tudjuk, hogy nem minden adatnak kell a boltban lennie. Például a helyi komponensállapot gyakran megfelelő hely az egyetlen összetevőre jellemző adatok tárolására.

de a helyadatok nem triviálisak. Ez az alkalmazás állapotának dinamikus és fontos része — az a fajta adat, amely a tárolóba tartozik. A boltban tartása lehetővé teszi a Redux luxust, mint például az időutazás hibakeresését, és könnyű hozzáférést biztosít bármely üzlethez csatlakoztatott alkatrészhez.

tehát hogyan helyezzük át a helyet a boltba?

nem lehet megkerülni azt a tényt, hogy a böngésző beolvassa és tárolja az előzményeket és a helyadatokat a window – ben, de amit tehetünk, hogy megőrizzük a helyadatok másolatát az áruházban, és szinkronban tartjuk a böngészővel.

nem ezt teszi a react-router-redux a React routernél?

igen, de csak a Redux DevTools időutazási képességeinek engedélyezéséhez. Az alkalmazás továbbra is a React routerben tárolt helyadatoktól függ:

history → React Router ↘ ↕ view Redux ↗

a react-router-redux használata a helyadatok olvasására az áruházból a React Router helyett nem ajánlott (potenciálisan ellentmondó igazságforrások miatt).

tudunk-e jobbat csinálni?

tudunk építeni egy alternatív útvonal modell — az egyik, hogy épül az alapoktól kezdve, hogy jól játsszon a Redux, amely lehetővé teszi számunkra, hogy olvassa el és frissítse a helyét A Redux módon — a store.getState()és store.dispatch()?

abszolút megtehetjük, és Redux-first routingnak hívják.

Redux-első útvonal

Redux-első routing egy változata pushState routing ami Redux A csillag a routing modell.

A Redux-first routing megoldás megfelel a következő kritériumoknak:

  • a helyet A Redux üzletben tartják.
  • a hely megváltozik a Redux műveletek elküldésével.
  • az alkalmazás beolvassa helyadatok kizárólag a boltban.
  • az áruház és a böngésző előzményei szinkronban vannak a színfalak mögött.

itt van egy alapötlet, hogy néz ki:

history ↕ Redux → router → view

várjon, nincs még két helymeghatározó adatforrás?

igen, de ha bízhatunk abban, hogy a böngésző előzményei és a Redux áruház szinkronban vannak, akkor az alkalmazásainkat csak az áruházból származó helyadatok olvasására építhetjük fel. Akkor, az alkalmazás szempontjából, csak egy igazságforrás van-a bolt.

hogyan valósítjuk meg A Redux-first útválasztást?

elkezdhetjük egy koncepcionális modell létrehozásával, a kliens oldali routing és a Redux adat életciklus modellek alapelemeinek egyesítésével.

az ügyféloldali útválasztási modell felülvizsgálata

az ügyféloldali útválasztás egy többlépcsős folyamat, amely a navigációval kezdődik és a rendereléssel végződik-maga az Útválasztás csak egy lépés ebben a folyamatban! Nézzük át a részleteket:

  • navigáció-minden a hely megváltoztatásával kezdődik. 2 típusú navigáció létezik: belső és külső. A belső navigáció az alkalmazáson belül történik(pl. a History API-n keresztül), míg a külső navigáció akkor történik, amikor a felhasználó interakcióba lép a böngésző navigációs sávjával, vagy belép az alkalmazásba egy külső webhelyről.
  • válasz a navigációra — amikor a hely megváltozik, az alkalmazás úgy reagál, hogy átadja az új helyet az útválasztónak. A régebbi útválasztási technikák a window.location lekérdezésre támaszkodtak ennek megvalósításához, de manapság a praktikus history.listen segédprogrammal rendelkezünk.
  • útválasztás — ezután az új hely illeszkedik a megfelelő oldaltartalomhoz. A kódot, amely ezt a lépést kezeli, útválasztónak hívják, és általában egy útvonal-konfigurációnak nevezett útvonalak és oldalak illesztésének bemeneti paraméterét veszi igénybe.
  • renderelés — végül a tartalom megjelenik az ügyfélen. Ezt a lépést természetesen egy olyan front-end keretrendszer/könyvtár kezelheti, mint a React.

vegye figyelembe, hogy az útválasztási könyvtáraknak nem kell kezelniük az útválasztási modell minden részét.

egyes könyvtárak, mint például a React Router és a Vue Router, megteszik — míg mások, mint például az Universal Router, kizárólag egyetlen aspektussal foglalkoznak (mint például az Útválasztás), így rugalmasságot biztosítanak a többi szempontban:

az útválasztási könyvtárak különböző felelősségi körökkel rendelkezhetnek. (Kattintson a nagyításhoz)

A Redux adat életciklus modell felülvizsgálata

A Redux egyirányú adatáramlási/életciklus-modellel büszkélkedhet, amely valószínűleg nem igényel bevezetést — de itt van egy rövid áttekintés a jó méréshez:

  • művelet — minden állapotváltozás egy Redux művelet elküldésével kezdődik (egy type – ot és opcionális hasznos terhet tartalmazó egyszerű objektum).
  • köztes szoftver — a művelet áthalad az áruház köztes láncán, ahol a műveletek elfoghatók és további viselkedések hajthatók végre. A Middlewares-t általában a Redux alkalmazások mellékhatásainak kezelésére használják.
  • reduktor — a művelet ezután eléri a gyökér reduktort, amely kiszámítja az áruház következő állapotát az előző állapot és a kapott művelet tiszta függvényeként. A gyökércsökkentő egyedi reduktorokból állhat, amelyek mindegyike kezeli az üzlet állapotának egy szeletét.
  • új állapot — az áruház elmenti a reduktor által visszaadott új állapotot, és értesíti előfizetőit a változásról (a Reactben a connect – en keresztül).
  • renderelés — végül az áruházhoz csatlakoztatott nézet újra megjeleníthető az új állapotnak megfelelően.

Redux-első útválasztási modell felépítése

a kliens oldali routing és a Redux adat életciklus modellek egyirányú jellege jól illeszkedik egy olyan egyesített modellhez, amely megfelel a Redux-first routing kritériumainak.

ebben a modellben az útválasztó feliratkozik az áruházra, a navigáció Redux műveletekkel történik, a böngésző előzményeinek frissítéseit pedig egy egyedi köztes szoftver kezeli. Vizsgáljuk meg a modell részleteit:

  • belső navigáció Redux műveletekkel — a History API közvetlen használata helyett a belső navigáció az előzmények navigációs módszereit tükröző 5 navigációs művelet egyikének elküldésével érhető el.
  • a böngészési előzmények frissítése köztes szoftveren keresztül — a köztes szoftver a navigációs műveletek elfogására és a böngészési előzmények frissítésének mellékhatásainak kezelésére szolgál. Mivel az új hely nem feltétlenül vagy könnyen ismert anélkül, hogy először megnézné a böngésző előzményeit(pl. go művelet esetén) a navigációs műveletek nem érik el a szűkítőt.
  • válasz a navigációra — a végrehajtás folyamata egy history hallgatóval folytatódik, amely válaszol a navigációra (mind a köztes szoftverből, mind a külső navigációból) egy második művelet elküldésével, amely tartalmazza az új helyet.
  • Helycsökkentő — a hallgató által elküldött művelet ezután eléri a helycsökkentőt, amely hozzáadja a helyet az áruházhoz. A helycsökkentő meghatározza a helyállapot alakját is.
  • csatlakoztatott útválasztás — az áruházhoz csatlakoztatott útválasztó ezután reaktív módon meghatározhatja az új oldal tartalmát, amikor értesítést kap az áruház helyének változásáról.
  • renderelés — végül az oldal újra megjeleníthető az új tartalommal.

vegye figyelembe, hogy ez nem az egyetlen módja A Redux-first routing megvalósításának — egyes változatok tartalmazzák a store enhancer és/vagy kiegészítő logika használatát a köztes szoftverben—, de ez egy egyszerű modell, amely lefedi az összes alapot.

alapvető megvalósítás

az imént vizsgált modellt követve valósítsuk meg a core API — t-az actions, middleware, listener és reducer.

a history csomagot belső függőségként fogjuk használni, és fokozatosan építjük fel a megoldást. Ha inkább követi a végeredményt, itt megtekintheti.

műveletek

kezdjük az 5 navigációs művelet meghatározásával, amelyek tükrözik az előzmények navigációs módszereit:

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

ezután határozzuk meg a middleware-t. Meg kell hallgatnia a navigációs műveleteket, meg kell hívnia a megfelelő history navigációs módszereket, majd meg kell akadályoznia, hogy a művelet elérje a szűkítőt — de minden más műveletet zavartalanul hagyjon:

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

ha még nem volt alkalma megírni vagy megvizsgálni a Redux köztes szoftver belsejét, nézze meg ezt a bevezetést.

előzmények figyelő

ezután szükségünk lesz egy history figyelőre, amely az új helyadatokat tartalmazó új művelet elküldésével reagál a navigációra.

először adjuk hozzá az új művelettípust és létrehozót. A hely érdekes részei a pathname, search és hash — tehát ezt fogjuk belefoglalni a hasznos teherbe:

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

akkor írjuk meg a hallgató funkciót:

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

készítünk egy kis kiegészítést-egy kezdeti locationChange küldést, hogy figyelembe vegyük az alkalmazás kezdeti bejegyzését (amelyet az előzmények hallgatója nem vesz fel):

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

szűkítő

ezután határozzuk meg a helycsökkentőt. Egyszerű állapotformát fogunk használni, és minimális munkát végzünk a szűkítőben:

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

Alkalmazási kód

végül, csatlakoztassuk az API-t az alkalmazás kódjához:

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

és ennyi az egész! A tiny (100 sornyi kód alatt) API használatával teljesítettük a Redux-first routing összes kritériumát:

  • a helyet A Redux üzletben tartják. 6458 >
  • a helyszín a Redux műveletek elküldésével módosul. 6458>
  • az alkalmazás kizárólag az áruházból olvassa be a helyadatokat. 6458>
  • az áruház és a böngésző előzményei szinkronban vannak a színfalak mögött. ✔

itt tekintheti meg az összes fájlt együtt-nyugodtan importálhatja őket a projektbe, vagy használhatja kiindulási pontként a saját megvalósításának kidolgozásához.

A redux-first-routing csomag

az API-t is összeraktam a redux-first-routing csomagba, amelyet npm install használhatsz ugyanúgy.

mksarge/redux-first-routing
redux-first-routing — minimális, keret-agnosztikus Alap A Redux-first routing megvalósításához.github.com

tartalmaz egy implementációt, amely hasonló az itt építetthez, de figyelemre méltó hozzáadásával lekérdezés elemzés a query-string csomagon keresztül.

várjon-mi a helyzet a tényleges útválasztási összetevővel?

lehet, hogy észrevette, hogy a redux-first-routing csak az útválasztási modell navigációs aspektusával foglalkozik:

azáltal, hogy leválasztottuk a navigációs szempontot az útválasztási modellünk többi aspektusától, némi rugalmasságot nyertünk — a redux-first-routing mind router-agnosztikus, mind framework-agnosztikus.

ezért párosíthatja egy olyan könyvtárral, mint a Universal Router, hogy teljes Redux-first routing megoldást hozzon létre bármely front-end keretrendszerhez:

kattintson ide A redux-first-routing + universal-router használatához.

vagy építhet véleményes kötéseket a választott keretrendszerhez — és ezt fogjuk tenni a React számára a cikk következő és utolsó szakaszában.

használat a React-Tel

fejezzük be a kutatásunkat azzal, hogy megvizsgáljuk, hogyan építhetünk üzlethez kapcsolódó komponenseket a deklaratív navigációhoz és útválasztáshoz a React – ben.

deklaratív navigáció

a navigációhoz használhatunk egy áruházhoz csatlakoztatott <Lin k/> komponenst, amely hasonló a React routerhez és más React routing megoldásokhoz.

egyszerűen felülbírálja a <a/> horgonyelem alapértelmezett viselkedését és a diszpécserhes a push műveletet, ha rákattint:

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

a teljesebb megvalósítást itt találhatja meg.

deklaratív útválasztás

bár a navigációs komponensnek nincs sok része, számtalan módon lehet megtervezni az útválasztási komponenst — ez minden útválasztási megoldás legérdekesebb része.

mi az a router, egyébként?

az útválasztót általában funkcióként vagy fekete dobozként tekintheti meg, két bemenettel és egy kimenettel:

route configuration ↘ matched content current location ↗

bár az Útválasztás és az azt követő renderelés külön lépésekben történhet, a React egyszerűvé és intuitívvá teszi, hogy ezeket deklaratív útválasztási API-ba csomagolja. Nézzünk két stratégiát ennek megvalósítására.

1. stratégia: egy monolitikus <Route r / > komponens

használhatunk egy monolitikus, áruházhoz csatlakoztatott <Router/> komponenst, amely:

  • útvonal-konfigurációs objektum elfogadása kellékeken keresztül
  • beolvassa a helyadatokat A Redux áruházból
  • kiszámítja az új tartalmat, amikor a hely megváltozik
  • rendereli/újra rendereli a tartalmat a megfelelő módon.

az útvonalkonfiguráció lehet egy egyszerű JavaScript-objektum, amely tartalmazza az összes megfelelő útvonalat és oldalt (központosított útvonalkonfiguráció).

így nézhet ki ez:

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

elég egyszerű, igaz? Nincs szükség beágyazott JSX útvonalakra — csak egyetlen útvonal-konfigurációs objektumra és egyetlen útválasztó-összetevőre.

ha ez a stratégia vonzó számodra, nézd meg a teljesebb megvalósításomat a redux-json-router könyvtárban. Csomagolja redux-first-routing és React kötéseket biztosít deklaratív navigációhoz és útválasztáshoz az eddig vizsgált stratégiák felhasználásával.

mksarge / redux-json-router
redux-json-router-deklaratív, Redux-első útvonal a React / Redux böngészőhöz applications.github.com

2. stratégia: Composable <Rout e / > components

míg a monolitikus komponens egyszerű módja lehet a deklaratív útválasztásnak a React-ben, határozottan nem ez az egyetlen módja.

a React összeállítható jellege egy másik érdekes lehetőséget is lehetővé tesz: a JSX használatával decentralizált módon határozhatja meg az útvonalakat. Természetesen a legjobb példa a React Router <Route/> API-ja:

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

más útválasztási könyvtárak is feltárják ezt az ötletet. Bár még nem volt alkalmam megtenni, nem látok okot arra, hogy egy hasonló API-t miért nem lehetett megvalósítani a redux-first-routing csomag tetején.

ahelyett, hogy a <BrowserRouter/>, the &lt által szolgáltatott helyadatokra támaszkodna; útvonal / > C komponensould si mply csatlakozzon az áruházhoz:

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

ha ez valami, amit érdekel az épület vagy a használata, tudassa velem a megjegyzéseket! Ha többet szeretne megtudni a különböző útvonalkonfigurációs stratégiákról, nézze meg ezt a bevezetést a React Router webhelyén.

következtetés

remélem, hogy ez a felfedezés segített elmélyíteni az ügyféloldali útválasztással kapcsolatos ismereteit, és megmutatta, milyen egyszerű ezt Redux módon megvalósítani.

ha teljes Redux útválasztási megoldást keres, használhatja a redux-first-routing csomagot a readme-ben felsorolt kompatibilis útválasztóval. És ha úgy találja, hogy személyre szabott megoldást kell kidolgoznia, remélhetőleg ez a bejegyzés jó kiindulási pontot adott erre.

ha többet szeretne megtudni a React és a Redux ügyféloldali útválasztásáról, nézze meg a következő cikkeket — ezek nagyban hozzájárultak ahhoz, hogy jobban megértsem az itt tárgyalt témákat:

  • hagyja, hogy az URL ne a beszélő által Tyler Thompson
  • lehet, hogy nem kell reagálni Router által Konstantin Tarkus
  • szükségem van még egy Routing Könyvtár? James K. Nelson
  • és számtalan informatív viták a react-router-redux kérdések.

a kliens oldali routing egy végtelen tervezési lehetőségekkel rendelkező tér, és biztos vagyok benne, hogy néhányan olyan ötletekkel játszottak, mint amilyeneket itt megosztottam. Ha folytatni szeretné a beszélgetést, örülök, hogy kapcsolatba léphetek veled a megjegyzésekben vagy a Twitteren keresztül. Köszönjük, hogy elolvasta!

Szerkesztés 22/06/17: Olvassa el ezt a cikket is redux-first-router, egy külön projekt, amely intelligens művelettípusokat használ az erőteljes útválasztási képességek eléréséhez.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.