by Michael Sargent

reitityskirjasto on keskeinen osa mitä tahansa monimutkaista, yksisivuista sovellusta. Jos kehität verkkosovelluksia Reactilla ja Reduxilla, olet todennäköisesti käyttänyt tai ainakin kuullut React-reitittimestä. Se on tunnettu react-reitityskirjasto ja loistava ratkaisu moniin käyttötapauksiin.

mutta React-reititin ei ole ainoa toimiva ratkaisu React/Redux-ekosysteemissä. Itse asiassa Reactille ja Reduxille on rakennettu valtavasti reititysratkaisuja, joista jokaisella on erilaiset sovellusliittymät, ominaisuudet ja tavoitteet — ja lista vain kasvaa. Sanomattakin on selvää, että asiakaspuolen reititys ei katoa lähiaikoina, ja huomisen reitityskirjastoissa on vielä paljon tilaa suunnittelulle.

tänään haluan kiinnittää huomionne reitityksen aiheeseen Reduxissa. Esittelen ja tehdä tapauksessa Redux – ensimmäinen reititys-paradigma, joka tekee Redux tähti reititys malli, ja yhteinen lanka joukossa monia Redux reititys ratkaisuja. Näytän, miten koota ydin, framework-Agnostic API alle 100 riviä koodia, ennen kuin tutkitaan vaihtoehtoja reaalimaailman käyttöön React ja muut front-end kehyksiä.

vähän historiaa

selaimessa sijainti (URL-tiedot) ja istuntohistoria (pino nykyisen selaimen välilehden vierailemia sijainteja) tallennetaan globaaliin window – objektiin. Niihin pääsee:

  • window.location (Sijaintirajapinta)
  • window.history (History API).

History API tarjoaa seuraavat historianavigointimenetelmät, jotka ovat merkittäviä niiden kyvystä päivittää selaimen historia ja sijainti ilman, että sivun uudelleenlatausta tarvitaan:

  • pushState(href) — työntää uuden sijainnin historiapinoon
  • replaceState(href) — korvaa pinon nykyisen sijainnin
  • back() — navigoi edelliseen paikkaan pinossa
  • forward() — navigoi seuraavaan paikkaan pinossa
  • go(index) — navigoi paikkaan pinossa, kumpaan tahansa suuntaan.

yhdessä historia-ja Sijaintirajapinnat mahdollistavat modernin asiakaspuolen reititysparadigman, joka tunnetaan nimellä pushState routing — tarinamme ensimmäinen päähenkilö.

nyt on melkein rikos mainita historia-ja Sijaintirajapinnat mainitsematta modernia käärekirjastoa, kuten history.

ReactTraining / history
Hallitse istuntohistoriaa JavaScriptgithub.com

history tarjoaa yksinkertaisen mutta tehokkaan API: n selaimen historian ja sijainnin yhdistämiseen, samalla kun se kattaa eri selaintoteutusten väliset epäjohdonmukaisuudet. Sitä käytetään vertaisena tai sisäisenä riippuvuutena monissa moderneissa reitityskirjastoissa, ja minä teen useita viittauksia siihen koko tässä artikkelissa.

Redux ja pushState Routing

tarinamme toinen päähenkilö on Redux. On vuosi 2017, joten säästän sinut esittelyltä ja menen suoraan asiaan:

käyttämällä tavallista pushState-reititystä Redux-sovelluksessa, jaamme sovellustilan kahteen verkkotunnukseen: selainhistoriaan ja Redux-kauppaan.

Tältä näyttää React Router, joka instantioi ja kietoo history:

history → React Router ↘ view Redux ↗

nyt tiedetään, ettei kaikkien tietojen tarvitse sijaita kaupassa. Esimerkiksi paikallinen komponenttitila on usein sopiva paikka tallentaa yksittäiselle komponentille ominaisia tietoja.

mutta sijaintitiedot eivät ole vähäpätöisiä. Se on dynaaminen ja tärkeä osa sovelluksen tilaa — sellaista dataa, joka kuuluu kauppaan. Sen pitäminen myymälässä mahdollistaa Redux-ylellisyyksien, kuten aikamatkustuksen virheenkorjauksen, ja helpon pääsyn mistä tahansa kauppaan liitetystä komponentista.

Joten miten siirrämme sijainnin kauppaan?

ei voi kiertää sitä tosiasiaa, että selain lukee ja tallentaa historia-ja sijaintitietoja window, mutta mitä voimme tehdä, on säilyttää kopio sijaintitiedoista kaupassa, ja pitää ne synkronoituna selaimen kanssa.

eikös react-router-redux tee niin React Routerille?

Kyllä, mutta vain Redux Devtoolien aikamatkakyvyn mahdollistamiseksi. Sovellus riippuu edelleen React-reitittimessä säilytettävistä sijaintitiedoista:

history → React Router ↘ ↕ view Redux ↗

react-router-redux käyttäminen myymälän sijaintitietojen lukemiseen React-reitittimen sijaan ei ole suositeltavaa (mahdollisesti ristiriitaisten totuuslähteiden vuoksi).

pystymmekö parempaan?

Voimmeko rakentaa vaihtoehtoisen reititysmallin — sellaisen, joka on rakennettu alusta asti pelaamaan hyvin Reduxin kanssa, jolloin voimme lukea ja päivittää sijainnin Reduxin tavalla — kanssa store.getState() ja store.dispatch()?

me ehdottomasti pystymme, ja sitä kutsutaan Redux-first routingiksi.

Redux – ensimmäinen reititys

Redux-first routing on muunnelma pushState-reitityksestä, joka tekee Reduxista reititysmallin tähden.

Redux-first routing-ratkaisu täyttää seuraavat kriteerit:

  • paikka pidetään Redux-myymälässä.
  • sijaintia muutetaan lähettämällä Redux-toiminnot.
  • sovellus lukee sijaintitiedot pelkästään kaupasta.
  • kaupan ja selaimen historiaa pidetään kulissien takana synkronoituna.

tässä perusidea siitä, miltä tuo näyttää:

history ↕ Redux → router → view

odota, eikö sijaintitietoja ole vielä kahta lähdettä?

Kyllä, mutta jos voimme luottaa siihen, että selainhistoria ja Redux store ovat synkronoituja, voimme rakentaa sovelluksemme vain lukemaan sijaintitietoja kaupasta. Sitten sovelluksen näkökulmasta on vain yksi totuuden lähde-kauppa.

miten Redux – ensimmäinen reititys onnistuu?

voimme aloittaa luomalla käsitteellisen mallin yhdistämällä asiakaspuolen reititys-ja Redux-tietojen elinkaarimallien peruselementit.

asiakaspuolen Reititysmallin tarkistaminen

asiakaspuolen reititys on monivaiheinen prosessi, joka alkaa navigoinnista ja päättyy renderöintiin — itse reititys on vain yksi vaihe tässä prosessissa! Kerrataan yksityiskohdat.:

  • navigointi-kaikki alkaa sijainnin muutoksella. On olemassa 2 tyyppisiä navigointi: sisäinen ja ulkoinen. Sisäinen navigointi tapahtuu sovelluksen sisältä (esim. History API: n kautta), kun taas ulkoinen navigointi tapahtuu, kun käyttäjä on vuorovaikutuksessa selaimen navigointipalkin kanssa tai syöttää sovelluksen ulkoiselta sivustolta.
  • navigointiin vastaaminen — kun sijainti muuttuu, sovellus vastaa välittämällä uuden sijainnin reitittimeen. Vanhemmat reititystekniikat tukeutuivat gallupeihin window.location tämän saavuttamiseksi, mutta nykyään käytössä on kätevä history.listen utility.
  • reititys-seuraavaksi uusi sijainti sovitetaan sen vastaavaan sivun sisältöön. Koodia, joka käsittelee tätä vaihetta kutsutaan reitittimeksi, ja se yleensä vie syöttöparametrin, joka vastaa reittejä ja sivuja kutsutaan reittikonfiguraatioksi.
  • renderöinti-lopuksi sisältö renderoidaan asiakkaalle. Tämä vaihe voidaan tietenkin hoitaa front-end framework / library kuten React.

huomaa, että reitityskirjastojen ei tarvitse käsitellä reititysmallin jokaista osaa.

jotkut kirjastot, kuten React Router ja Vue Router, do — kun taas toiset, kuten Universal Router, käsittelevät vain yhtä näkökohtaa (kuten reititystä), mikä tarjoaa joustavuutta muissa asioissa:

Reitityskirjastoilla voi olla erilaisia vastuualueita. (Klikkaa suurentaaksesi)

Redux-tietojen Elinkaarimallin tarkistaminen

Redux tarjoaa yksisuuntaisen tiedonkulun / elinkaarimallin, joka ei todennäköisesti tarvitse esittelyä — mutta tässä lyhyt katsaus hyvään mittaukseen:

  • toiminto – kaikki tilan muutokset aloitetaan Redux-toiminnolla (tavallinen objekti, joka sisältää type ja valinnaisen hyötykuorman).
  • Middleware — toiminto kulkee kaupan väliketjussa, jossa voidaan siepata toimintoja ja tehdä lisäkäyttäytymistä. Middlewares käytetään yleisesti käsittelemään sivuvaikutuksia Redux sovelluksia.
  • Reducer — toiminto saavuttaa tämän jälkeen juurivähennyslaitteen, joka laskee myymälän seuraavan tilan edellisen tilan ja vastaanotetun toiminnon puhtaana funktiona. Juurivähennys voi koostua yksittäisistä pelkistimistä, jotka kukin käsittelevät siivun kaupan tilasta.
  • Uusi valtio-kauppa pelastaa pelkistäjän palauttaman uuden valtion ja ilmoittaa Tilaajilleen muutoksesta (in React, via connect).
  • renderöinti-lopulta kauppaan liitetty näkymä voi renderöidä uudelleen uuden tilan mukaisesti.

Redux-First Routing-mallin rakentaminen

asiakaspuolen reititys-ja Redux-tietojen elinkaarimallien yksisuuntainen luonne sopii hyvin yhteen yhdistetyn mallin kanssa, joka täyttää Redux-ensimmäinen reititys-kriteerit.

tässä mallissa reititin tilataan kauppaan, navigointi tapahtuu Redux-toiminnoilla ja selainhistorian päivitykset hoidetaan mukautetulla väliohjelmalla. Tarkastellaan yksityiskohtia tämän mallin:

  • sisäinen navigointi Redux — toimintojen kautta-sen sijaan, että käyttäisit suoraan History API: ta, sisäinen navigointi saavutetaan lähettämällä yksi viidestä navigointitoiminnosta, jotka peilaavat historianavigointimenetelmiä.
  • selainhistorian päivittäminen middleware — ohjelman kautta-väliohjelmistoa käytetään navigointitoimintojen sieppaamiseen ja selainhistorian päivittämisen sivuvaikutusten käsittelyyn. Koska uusi sijainti ei ole välttämättä tai helposti tiedossa kysymättä ensin selainhistoriaa (esim. jos kyseessä on go toimenpide), suunnistustoimia estetään saavuttamasta pelkistintä.
  • navigointiin vastaaminen — suoritusvirtaa jatkaa history kuuntelija, joka vastaa navigointiin (sekä väliohjelmasta että ulkoisesta navigoinnista) lähettämällä toisen toiminnon, joka sisältää uuden sijainnin.
  • Sijaintivähennys — kuulijan lähettämä toiminto saavuttaa tämän jälkeen sijaintivähennyslaitteen, joka lisää sijainnin myymälään. Paikkavähentäjä määrittää myös sijaintitilan muodon.
  • Connected routing — kauppaan liitetty reititin voi sitten määrittää uuden sivun sisällön reaktiivisesti, kun sille ilmoitetaan myymälän sijainnin muutoksesta.
  • renderöinti-lopuksi sivu voidaan renderoida uudelleen uuden sisällön kanssa.

huomaa, että tämä ei ole ainoa tapa toteuttaa Redux-ensimmäinen reititys — joissakin muunnelmissa on kaupan tehostajan käyttö ja/tai lisälogiikka väliohjelmassa — mutta se on yksinkertainen malli, joka kattaa kaikki perusteet.

Perustoteutus

seuraten juuri tarkastelemaamme mallia, Otetaan käyttöön core API-toiminnot, väliohjelmisto, kuuntelija ja reducer.

käytämme history pakettia sisäisenä riippuvuutena ja rakennamme ratkaisun vähitellen. Jos haluat seurata lopputulosta, voit katsoa sen täältä.

toimet

aloitamme määrittelemällä 5 navigointitoimintoa, jotka peilaavat historian navigointimenetelmiä:

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

seuraavaksi määritellään Välisarja

. Sen pitäisi siepata navigointitoiminnot, kutsua vastaavat history navigointimenetelmät, sitten estää toimintaa saavuttamasta vähennintä — mutta jättää kaikki muut toimet häiritsemättä:

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

jos sinulla ei ole ollut mahdollisuutta kirjoittaa tai tutkia sisäosat Redux middleware ennen, tutustu tähän esittelyyn.

historian kuuntelija

seuraavaksi tarvitaan history kuuntelija, joka vastaa navigointiin lähettämällä uuden toiminnon, joka sisältää uudet sijaintitiedot.

lisätään ensin uusi toimintatyyppi ja luoja. Kohteen mielenkiintoiset osat ovat pathname, search ja hash — joten se otetaan mukaan hyötykuormaan:

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

sitten kirjoitetaan kuulijan funktio:

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

teemme yhden pienen lisäyksen-alkuperäisen locationChange lähetyksen, joka vastaa hakemukseen tehtyä ensimmäistä merkintää (jota historian kuuntelija ei nouda):

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

seuraavaksi määritellään paikkavähennys. Käytämme yksinkertaista tilamuotoa ja teemme minimaalista työtä pelkistimessä.:

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

sovelluskoodi

lopuksi kytketään API sovelluskoodiin:

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

ja siinä kaikki! Käyttämällä tiny (alle 100 riviä koodia) API, olemme täyttäneet kaikki kriteerit Redux-ensimmäinen reititys:

  • paikka pidetään Redux-myymälässä. ✔
  • sijaintia muutetaan lähettämällä Redux-toiminnot. ✔
  • sovellus lukee sijaintitiedot ainoastaan kaupasta. ✔
  • tallentaa ja selainhistoria pidetään synkronoituna kulissien takana. ✔

Katso kaikki tiedostot yhdessä täältä — voit vapaasti tuoda ne projektiisi tai käyttää sitä lähtökohtana oman toteutuksesi kehittämisessä.

redux-ensireitityspaketti

olen myös koonnut API: n redux-first-routing pakettiin, jota saa npm install käyttää samalla tavalla.

mksarge/redux-first-routing
redux-first-routing — minimaalinen, framework-agnostinen pohja Redux-first-reitityksen toteuttamiselle.github.kom

se sisältää samanlaisen toteutuksen kuin me rakensimme täällä, mutta huomattavaa on myös kyselyn jäsentäminen query-string – paketin kautta.

odota-entä varsinainen reitityskomponentti?

olet ehkä huomannut, että redux-first-routing koskee vain reititysmallin navigointia:

irrottamalla navigaationäkökulman muista reititysmallimme näkökohdista olemme saaneet joustavuutta-redux-first-routing on sekä reititin-agnostinen että framework-agnostinen.

voit siis yhdistää sen kirjastoon, kuten Yleisreitittimeen, luodaksesi täydellisen Redux-first-reititysratkaisun mihin tahansa front-end-kehykseen:

Klikkaa tästä aloittaaksesi Redux-first-routing + universal-router.

tai, voit rakentaa itsepintaisia sidoksia oman valinnan puitteisiin — ja niin me teemme Reagoidaksemme tämän artikkelin seuraavassa ja viimeisessä osassa.

käyttö React-valmisteen kanssa

lopetetaan etsintä tarkastelemalla, miten voisimme rakentaa kauppaan liitettyjä komponentteja reactissa tapahtuvaa navigointia ja reititystä varten.

Deklaratiivinen navigointi

navigointiin voidaan käyttää myymäläyhteyttä <Link/> vastaavaa komponenttia kuin React-reitittimessä ja muissa React-reititysratkaisuissa.

se yksinkertaisesti ohittaa ankkurielementin <a/> ja dispatches työntötoiminnon napsautettaessa:

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

löydät kattavamman toteutuksen täältä.

Deklaratiivinen reititys

vaikka navigaatiokomponentissa ei ole paljoa, on lukemattomia tapoja suunnitella reitityskomponentti — mikä tekee siitä kiinnostavimman osan reititysratkaisussa.

mikä reititin oikein on?

reitittimen voi yleensä nähdä funktiona tai mustana laatikkona, jossa on kaksi tuloa ja yksi ulostulo:

route configuration ↘ matched content current location ↗

vaikka reititys ja sitä seuraava renderöinti voi tapahtua eri vaiheissa, React tekee niistä helpon ja intuitiivisen niputtaa ne yhteen deklaratiiviseksi reititysrajapinnaksi. Katsotaanpa kaksi strategiaa tämän saavuttamiseksi.

strategia 1: monoliittinen <Route r / > komponentti

Voimme käyttää monoliittista, varastokytkettyä <Route r / > komponenttia, joka:

  • hyväksyy reittikonfiguraation kohteen rekvisiitan kautta
  • lukee Redux Storen sijaintitiedot
  • laskee uuden sisällön aina, kun sijainti muuttuu
  • renders/re-renders the content as appropriate.

reittikonfiguraatio voi olla tavallinen JavaScript-objekti, joka sisältää kaikki vastaavat polut ja sivut (keskitetty reittikonfiguraatio).

tältä tämä voisi näyttää:

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

aika yksinkertaista, vai mitä? Ei tarvitse sisäkkäisiä JSX reitit – vain yhden reitin kokoonpano objekti, ja yhden reitittimen komponentti.

jos tämä strategia vetoaa sinuun, tutustu kattavampaan toteutukseeni redux-json-router kirjastosta. Se käärii redux-first-routing ja tarjoaa React-sidokset deklaratiiviseen navigointiin ja reititykseen käyttäen tähän mennessä tarkastelemiamme strategioita.

mksarge / redux-json-router
redux-json-router-Declarative, Redux-first routing For React/Redux-browser applications.github.com

strategia 2: Kompositio <Route / > komponentit

vaikka monoliittinen komponentti voi olla yksinkertainen tapa saavuttaa deklaratiivinen reititys Reactissa, se ei todellakaan ole ainoa tapa.

Reactin koostumuksellinen luonne antaa toisen mielenkiintoisen mahdollisuuden: JSX: n käyttäminen reittien määrittelyyn hajautetusti. Malliesimerkki on tietysti React Routerin <Route / > API:

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

myös muut reitityskirjastot tutkivat tätä ideaa. Vaikka minulla ei ole ollut siihen mahdollisuutta, en näe mitään syytä, miksi vastaavaa API: ta ei voisi toteuttaa redux-first-routing – paketin päälle.

<BrowserRoute r / >, the &lt; reitti / > komponentti Could simply yhdistä kauppaan:

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

jos se on jotain, että olet kiinnostunut rakentamaan tai käyttämään, kerro minulle kommenteissa! Lisätietoja eri reittikonfiguraatiostrategioista löytyy React Routerin verkkosivuilta.

Conclusion

toivon, että tämä etsintä on auttanut syventämään tietojasi asiakaspuolen reitityksestä ja osoittanut, kuinka yksinkertaista on toteuttaa se Redux-tavalla.

jos etsit täydellistä Redux-reititysratkaisua, voit käyttää redux-first-routing – pakettia, jossa on readmessa mainittu yhteensopiva reititin. Ja jos huomaat tarvitse kehittää räätälöity ratkaisu, Toivottavasti tämä viesti on antanut sinulle hyvän lähtökohdan tehdä niin.

jos haluat lisätietoja asiakaspuolen reitityksestä Reactissa ja Reduxissa, tutustu seuraaviin artikkeleihin-ne auttoivat minua ymmärtämään paremmin aiheita, joita käsittelin täällä:

  • let the URL Do the Talking by Tyler Thompson
  • you Might Not Need React Router by Konstantin Tarkus
  • Do I Even Need a Routing Library? James K. Nelson
  • ja lukemattomat informatiiviset keskustelut react-router-redux numeroissa.

asiakaspuolen reititys on tila, jossa on loputtomasti suunnittelumahdollisuuksia, ja olen varma, että jotkut teistä ovat leikitelleet samanlaisilla ideoilla kuin minä olen täällä jakanut. Jos haluat jatkaa keskustelua, otan mielelläni yhteyttä sinuun kommenteissa tai Twitterin kautta. Kiitos lukemisesta!

Edit 22/06/17: Tutustu myös tähän artikkeliin redux-first-router: stä, erillisestä projektista, joka käyttää älykkäitä toimintatyyppejä tehokkaiden reititysominaisuuksien saavuttamiseksi.

Vastaa

Sähköpostiosoitettasi ei julkaista.