route(root, defaultRoute, routes)
Leírás
Navigáció az alkalmazáson belüli oldalak között.
var Home = {
view: function () {
return 'Welcome';
},
};
m.route(document.body, '/home', {
'/home': Home, // defines `https://localhost/#!/home`
});
Egy alkalmazáson belül csak egy m.route
hívás lehet.
Szignatúra
m.route(root, defaultRoute, routes)
Argumentum | Típus | Kötelező | Leírás |
---|---|---|---|
root | Element | Igen | Egy DOM elem, amely a részfa szülőcsomópontja lesz. |
defaultRoute | String | Igen | Az az útvonal, amelyre átirányítás történik, ha az aktuális URL nem egyezik egyik útvonallal sem. Fontos megjegyezni, hogy ez nem a kezdeti útvonal. A kezdeti útvonal a böngésző címsorában lévő URL lesz. |
routes | Object<String,Component|RouteResolver> | Igen | Egy objektum, amelynek kulcsai útvonal karakterláncok, értékei pedig komponensek vagy egy RouteResolver. |
returns | Visszatér: undefined |
Hogyan kell olvasni a szignatúrákat
Statikus tagok
m.route.set
Átirányít egyező útvonalra, vagy az alapértelmezett útvonalra, ha nem található egyező útvonal. Aszinkron újrarajzolást vált ki az összes csatolási ponton.
m.route.set(path, params, options)
Argumentum | Típus | Kötelező | Leírás |
---|---|---|---|
path | String | Igen | Az útvonal neve, előtag nélkül. Az útvonal tartalmazhat paramétereket, amelyek a params értékeivel vannak behelyettesítve. |
params | Object | Nem | Útválasztási paraméterek. Ha a path útválasztási paraméterhelyeket tartalmaz, akkor ennek az objektumnak a tulajdonságai behelyettesítésre kerülnek az útvonal karakterláncba. |
options.replace | Boolean | Nem | Meghatározza, hogy új előzménybejegyzés jöjjön-e létre, vagy a jelenlegi legyen-e lecserélve. Alapértelmezés szerint false . |
options.state | Object | Nem | A state objektum, amelyet az alapul szolgáló history.pushState / history.replaceState hívásnak kell átadni. Ez az állapotobjektum elérhetővé válik a history.state tulajdonságban, és egyesül az útválasztási paraméterek objektumával. Vegyük figyelembe, hogy ez az opció csak akkor működik, ha a pushState API-t használja, de figyelmen kívül hagyja, ha az útválasztó hashchange módba esik vissza (azaz, ha a pushState API nem érhető el). |
options.title | String | Nem | A title karakterlánc, amelyet az alapul szolgáló history.pushState / history.replaceState hívásnak kell átadni. |
returns | Visszatér: undefined |
Ne feledd, hogy a .set
használatakor a params
-szal az útvonalat is definiálnod kell:
var Article = {
view: function (vnode) {
return 'This is article ' + vnode.attrs.articleid;
},
};
m.route(document.body, {
'/article/:articleid': Article,
});
m.route.set('/article/:articleid', { articleid: 1 });
m.route.get
Visszaadja az utolsó teljesen feloldott útvonalat, az előtag nélkül. Eltérhet a címsorban megjelenített útvonaltól, amíg egy aszinkron útvonal feloldásra vár.
path = m.route.get()
Argumentum | Típus | Kötelező | Leírás |
---|---|---|---|
returns | String | Visszaadja az utolsó teljesen feloldott útvonalat. |
m.route.prefix
Definiál egy útválasztó előtagot. Az útválasztó előtag az URL egy része, amely meghatározza az útválasztó által használt stratégiát.
m.route.prefix = prefix
Argumentum | Típus | Kötelező | Leírás |
---|---|---|---|
prefix | String | Igen | Az az előtag, amely szabályozza a Mithril által használt útválasztási stratégiát. |
Ez egy egyszerű tulajdonság, így olvashatod és írhatod is.
m.route.Link
Ez a komponens egy dinamikus útvonalas linket hoz létre. Lényege, hogy a
linkeket hozzon létre helyi href
-ekkel, amelyek az útvonal előtagot figyelembe véve átalakulnak.
m(m.route.Link, { href: '/foo' }, 'foo');
// Hacsak az m.route.prefix nem változott az alapértelmezett stratégiáról, a következőre renderel:
// <a href="#!/foo">foo</a>
A linkek a következő speciális attribútumokat fogadják el:
- A
selector
az, ami azm
első argumentumaként kerülne átadásra: bármilyen szelektort érvényes, beleértve a nema
elemeket is. - A
params
és azoptions
azonos nevű argumentumok, mint am.route.set
-ben definiáltak. - A
disabled
, hatrue
, letiltja az útválasztási viselkedést és a hozzá tartozóonclick
kezelőt, és egydata-disabled="true"
attribútumot csatol a kisegítő lehetőségekhez; ha az elem egya
, ahref
el lesz távolítva.
Az útválasztási viselkedés nem akadályozható meg az eseménykezelő API-val. Használd helyette a disabled
attribútumot.
m(
m.route.Link,
{
href: '/foo',
selector: 'button.large',
disabled: true,
params: { key: 'value' },
options: { replace: true },
},
'link name'
);
// A következőre renderel:
// <button disabled aria-disabled="true" class="large">link name</button>
vnode = m(m.route.Link, attributes, children)
Argumentum | Típus | Kötelező | Leírás |
---|---|---|---|
attributes.href | Object | Igen | A navigálni kívánt célútvonal. |
attributes.disabled | Boolean | Nem | Akadálymentesen letiltja az elemet. |
attributes.selector | String|Object|Function | Nem | Egy szelektort a m -hez, alapértelmezés szerint "a" . |
attributes.options | Object | Nem | Beállítja a m.route.set -nek átadott options -t. |
attributes.params | Object | Nem | Beállítja a m.route.set -nek átadott params -ot. |
attributes | Object | Nem | Bármely más attribútum, amelyet továbbítani kell az m -nek. |
children | Array<Vnode>|String|Number|Boolean | Nem | Gyermek vnode-ok ehhez a linkhez. |
returns | Vnode | Egy vnode. |
m.route.param
Lekér egy útvonalparamétert az utolsó teljesen feloldott útvonalból. Az útvonalparaméter egy kulcs-érték pár. Az útvonalparaméterek a következő helyekről származhatnak:
- útvonal interpolációk (pl. ha egy útvonal
/users/:id
, és/users/1
-re oldódik fel, akkor az útvonalparaméternek van egyid
kulcsa és egy"1"
értéke) - útválasztó lekérdezési karakterláncok (pl. ha az útvonal
/users?page=1
, akkor az útvonalparaméternek van egypage
kulcsa és egy"1"
értéke) history.state
(pl. ha ahistory.state
{foo: "bar"}
, akkor az útvonalparaméternek van egyfoo
kulcsa és egy"bar"
értéke)
value = m.route.param(key)
Argumentum | Típus | Kötelező | Leírás |
---|---|---|---|
key | String | Nem | Egy útvonalparaméter neve (pl. id a /users/:id útvonalban, vagy page a /users/1?page=3 útvonalban, vagy egy kulcs a history.state -ben). |
returns | String|Object | Visszaadja a megadott kulcs értékét. Ha nincs megadva kulcs, akkor egy objektumot ad vissza, amely tartalmazza az összes interpolációs kulcsot. |
Vegye figyelembe, hogy egy RouteResolver
onmatch
függvényében az új útvonal még nincs teljesen feloldva, és az m.route.param()
az előző útvonal paramétereit adja vissza, ha vannak ilyenek. Az onmatch
az új útvonal paramétereit kapja meg argumentumként.
m.route.SKIP
Egy speciális érték, amelyet egy útvonal feloldó onmatch
hívásából lehet visszaadni a következő útvonalra ugráshoz.
RouteResolver
A RouteResolver
egy nem komponens objektum, amely tartalmaz egy onmatch
metódust és/vagy egy render
metódust. Mindkét metódus opcionális, de legalább az egyiknek jelen kell lennie.
Ha egy objektum komponensként azonosítható (egy view
metódus jelenléte vagy egy function
/class
által), akkor akkor is annak fogják tekinteni, ha rendelkezik onmatch
vagy render
metódusokkal. Mivel a RouteResolver
nem egy komponens, nincsenek életciklus metódusai.
Általános szabályként a RouteResolver
eknek ugyanabban a fájlban kell lenniük, mint az m.route
hívásnak, míg a komponens definícióknak saját moduljaikban kell lenniük.
routeResolver = {onmatch, render}
Komponensek használatakor úgy gondolhatsz rájuk, mint egy speciális cukorra ehhez az útvonal feloldóhoz, feltételezve, hogy a komponensed Home
:
var routeResolver = {
onmatch: function () {
return Home;
},
render: function (vnode) {
return [vnode];
},
};
routeResolver.onmatch
Az onmatch
hook akkor hívódik meg, amikor az útválasztónak meg kell találnia egy renderelendő komponenst. Útvonalváltozáskor egyszer hívódik meg, de a későbbi, ugyanazon az útvonalon történő újrarajzolásokkor nem. Használható logika futtatására a komponens inicializálása előtt (például hitelesítési logika, adat előtöltés, átirányítási analitika követés stb.).
Ez a metódus lehetővé teszi, hogy aszinkron módon definiáljuk, hogy melyik komponens kerül renderelésre, így alkalmas a kód felosztására és az aszinkron modul betöltésére. Egy komponens aszinkron rendereléséhez adj vissza egy ígéretet, amely egy komponensre oldódik fel.
Az onmatch
-ról további információkért lásd a haladó komponens feloldás szekciót.
routeResolver.onmatch(args, requestedPath, route)
Argumentum | Típus | Leírás |
---|---|---|
args | Object | Az útválasztási paraméterek. |
requestedPath | String | Az utolsó útválasztási művelet által kért útválasztó útvonal, beleértve az interpolált útválasztási paraméterértékeket, de az előtag nélkül. Amikor az onmatch meghívásra kerül, az útvonal feloldása nem fejeződött be, és az m.route.get() még mindig az előző útvonalat adja vissza. |
route | String | Az utolsó útválasztási művelet által kért útválasztó útvonal, az interpolált útválasztási paraméterértékek nélkül. |
returns | Component|\Promise<Component>|undefined | Visszaad egy komponenst vagy egy ígéretet, amely egy komponensre oldódik fel. |
Ha az onmatch
egy komponenst vagy egy ígéretet ad vissza, amely egy komponensre oldódik fel, akkor ez a komponens kerül felhasználásra a RouteResolver
render
metódusának első argumentumában a vnode.tag
-ként. Ellenkező esetben a vnode.tag
"div"
-re van állítva. Hasonlóképpen, ha az onmatch
metódus elmarad, a vnode.tag
szintén "div"
.
Ha az onmatch
egy ígéretet ad vissza, amelyet elutasítanak, az útválasztó visszairányít a defaultRoute
-ra. Ezt a viselkedést felülbírálhatod a .catch
meghívásával az ígéretláncon, mielőtt visszaadnád.
routeResolver.render
A render
metódus minden újrarajzoláskor meghívásra kerül, ha az útvonal egyezik. Hasonló a komponensek view
metódusához, és azért létezik, hogy egyszerűsítse a komponens kompozíciót. Azt is lehetővé teszi, hogy felülírjuk a Mithril.js alapértelmezett viselkedését, amely a teljes részfát lecseréli.
vnode = routeResolver.render(vnode)
Argumentum | Típus | Leírás |
---|---|---|
vnode | Object | Egy vnode, amelynek attribútum objektuma útválasztási paramétereket tartalmaz. Ha az onmatch nem ad vissza egy komponenst vagy egy ígéretet, amely egy komponensre oldódik fel, a vnode tag mezője alapértelmezés szerint "div" . |
vnode.attrs | Object | Egy URL paraméterértékek térképe. |
returns | Array<Vnode>|Vnode | A renderelendő vnode-ok. |
A vnode
paraméter egyszerűen m(Component, m.route.param())
-ként van definiálva, ahol a Component
az útvonal feloldott komponense (az routeResolver.onmatch
után), és az m.route.param()
a itt dokumentáltak szerint. Ha kihagyod ezt a metódust, az alapértelmezett visszatérési érték [vnode]
, egy fragmentbe csomagolva, így használhatsz kulcsparamétereket. Egy :key
paraméterrel kombinálva egy egyelemes kulcsolt fragment lesz, mivel valami olyasmire renderelődik, mint [m(Component, {key: m.route.param("key"), ...})]
.
Hogyan működik
Az útválasztás egy olyan rendszer, amely lehetővé teszi az egyoldalas alkalmazások (SPA) létrehozását, azaz olyan alkalmazásokét, amelyek "oldalról" "oldalra" tudnak menni anélkül, hogy teljes böngészőfrissítést okoznának.
Lehetővé teszi a zökkenőmentes navigációt, miközben megőrzi az egyes oldalak egyedi könyvjelzővel való ellátásának lehetőségét, valamint az alkalmazás böngésző előzmény mechanizmusán keresztüli navigálásának lehetőségét.
Az oldalak frissítése nélküli útválasztást részben a history.pushState
API teszi lehetővé. Ennek az API-nak a használatával programozottan megváltoztatható a böngésző által megjelenített URL egy oldal betöltése után, de az alkalmazásfejlesztő felelőssége annak biztosítása, hogy egy adott URL-re való navigálás hideg állapotból (pl. egy új lap) a megfelelő jelölést renderelje.
Útválasztási stratégiák
Az útválasztási stratégia meghatározza, hogy egy könyvtár hogyan valósíthatja meg az útválasztást. Három általános stratégia létezik, amelyeket felhasználhatunk egy SPA útválasztási rendszer megvalósítására, és mindegyiknek más-más buktatói vannak:
m.route.prefix = '#!'
(alapértelmezett) – Az URL fragment azonosítójának (más néven hash) részének használata. Az ezt a stratégiát használó URL általában a következőképpen néz ki:https://localhost/#!/page1
.m.route.prefix = '?'
– A lekérdezési karakterlánc használata. Az ezt a stratégiát használó URL általában a következőképpen néz ki:https://localhost/?/page1
.m.route.prefix = ''
– Az útvonalnév használata. Az ezt a stratégiát használó URL általában a következőképpen néz ki:https://localhost/page1
.
A hash stratégia garantáltan működik azokban a böngészőkben, amelyek nem támogatják a history.pushState
-et, mert vissza tud esni az onhashchange
használatára. Használd ezt a stratégiát, ha a hasheket tisztán helyi szinten szeretnéd tartani.
A lekérdezési karakterlánc stratégia lehetővé teszi a szerveroldali észlelést, de nem jelenik meg normál útvonalként. Használd ezt a stratégiát, ha támogatni szeretnéd és potenciálisan észlelni szeretnéd a horgonyzott linkeket szerveroldalon, és nem tudod elvégezni az útvonalnév stratégia támogatásához szükséges változtatásokat (például ha Apache-ot használsz, és nem tudod módosítani a .htaccess
-edet).
Az útvonalnév stratégia a legtisztább megjelenésű URL-eket eredményezi, de megköveteli a szerver beállítását, hogy az egyoldalas alkalmazás kódját minden olyan URL-ről kiszolgálja, amelyre az alkalmazás képes útvonalat létrehozni. Használd ezt a stratégiát, ha tisztább megjelenésű URL-eket szeretnél.
A hash stratégiát használó egyoldalas alkalmazások gyakran használják azt a konvenciót, hogy felkiáltójelet tesznek a hash után, jelezve, hogy a hashet útválasztási mechanizmusként használják, és nem horgonyokhoz való hivatkozásra. A #!
karakterlánc hashbang néven ismert.
Az alapértelmezett stratégia a hashbangot használja.
Tipikus használat
Általában létre kell hoznod néhány komponenst, hogy az útvonalakhoz rendelhesd őket:
var Home = {
view: function () {
return [m(Menu), m('h1', 'Home')];
},
};
var Page1 = {
view: function () {
return [m(Menu), m('h1', 'Page 1')];
},
};
A fenti példában két komponens van: Home
és Page1
. Mindegyik tartalmaz egy menüt és valamilyen szöveget. A menü maga is komponensként van definiálva az ismétlés elkerülése érdekében:
var Menu = {
view: function () {
return m('nav', [
m(m.route.Link, { href: '/' }, 'Home'),
m(m.route.Link, { href: '/page1' }, 'Page 1'),
]);
},
};
Most már definiálhatunk útvonalakat, és hozzárendelhetjük a komponenseinket:
m.route(document.body, '/', {
'/': Home,
'/page1': Page1,
});
Itt két útvonalat adunk meg: /
és /page1
, amelyek a megfelelő komponenseiket renderelik, amikor a felhasználó az egyes URL-ekre navigál.
Navigálás különböző útvonalakra
A fenti példában a Menu
komponensnek két m.route.Link
-je van. Ez létrehoz egy elemet, alapértelmezés szerint egy <a>
-t, és beállítja úgy, hogy ha a felhasználó rákattint, akkor az önmagában egy másik útvonalra navigáljon. Nem navigál távolról, csak helyben.
Programozott módon is navigálhat az m.route.set(route)
segítségével. Például m.route.set("/page1")
.
Az útvonalak közötti navigáláskor az útválasztó automatikusan kezeli az előtagot. Más szóval, hagyd ki a hashbang #!
-t (vagy bármilyen előtagot, amire az m.route.prefix
-et beállítottad), amikor Mithril.js útvonalakat linkelsz, beleértve az m.route.set
-ben és az m.route.Link
-ben is.
Ne feledd, hogy a komponensek közötti navigáláskor a teljes részfát lecseréli. Használj útvonal feloldót render
metódussal, ha csak a részfát szeretnéd javítani.
Útválasztási paraméterek
Néha szeretnénk, hogy egy változó azonosító vagy hasonló adat megjelenjen az útvonalban, de nem akarunk explicit módon külön útvonalat megadni minden lehetséges azonosítóhoz. Ennek eléréséhez a Mithril.js támogatja a paraméterezett útvonalakat:
var Edit = {
view: function (vnode) {
return [m(Menu), m('h1', 'Editing ' + vnode.attrs.id)];
},
};
m.route(document.body, '/edit/1', {
'/edit/:id': Edit,
});
A fenti példában egy /edit/:id
útvonalat definiáltunk. Ez létrehoz egy dinamikus útvonalat, amely megfelel minden olyan URL-nek, amely /edit/
-tel kezdődik, és amelyet valamilyen adat követ (pl. /edit/1
, edit/234
stb.). Az id
érték ezután a komponens vnode attribútumaként van hozzárendelve (vnode.attrs.id
).
Lehetőség van több argumentumot is megadni egy útvonalban, például a /edit/:projectID/:userID
a komponens vnode attribútum objektumán a projectID
és a userID
tulajdonságokat eredményezné.
Kulcsparaméter
Amikor egy felhasználó egy paraméterezett útvonalról ugyanarra az útvonalra navigál egy másik paraméterrel (pl. /page/1
-ről /page/2
-re való áttérés egy /page/:id
útvonal esetén), a komponens nem kerülne újból létrehozásra a semmiből, mivel mindkét útvonal ugyanarra a komponensre oldódik fel, és így egy virtuális dom helyben történő diff-et eredményez. Ennek mellékhatása az onupdate
hook aktiválása az oninit
/oncreate
helyett. Azonban viszonylag gyakori, hogy egy fejlesztő szinkronizálni szeretné a komponens újbóli létrehozását az útvonalváltozási eseményhez.
Ennek eléréséhez kombinálhatod az útvonalparaméterezést a kulcsokkal egy kényelmes megoldás érdekében:
m.route(document.body, '/edit/1', {
'/edit/:key': Edit,
});
Ez azt jelenti, hogy az útvonal gyökérkomponensének vnode-jában a key
útvonalparaméter elérhető lesz. Az útvonalparaméterek a vnode-ban attrs
-okká válnak. Így, amikor egyik oldalról a másikra ugrunk, a key
megváltozik, és a komponens a semmiből újra létrejön (mivel a kulcs azt mondja a virtuális dom motornak, hogy a régi és az új komponensek különböző entitások).
Ezt az ötletet tovább lehet vinni olyan komponensek létrehozására, amelyek újratöltéskor újra létrehozzák magukat:
m.route.set(m.route.get(), {key: Date.now()})
Vagy akár használhatod a history state
funkciót az újratölthető komponensek eléréséhez anélkül, hogy az URL-t szennyeznéd:
m.route.set(m.route.get(), null, {state: {key: Date.now()}})
Ne feledd, hogy a kulcsparaméter csak komponens útvonalakon működik. Ha útvonal feloldót használsz, akkor egy egyelemes kulcsolt fragmentet kell használnod, átadva a key: m.route.param("key")
-t, hogy ugyanazt elérd.
Variadikus útvonalak
Használhatunk variadikus útvonalakat is, azaz egy olyan útvonalra, amelynek argumentuma URL útvonalneveket tartalmaz, amelyek perjeleket tartalmaznak:
m.route(document.body, '/edit/pictures/image.jpg', {
'/edit/:file...': Edit,
});
404-es hibák kezelése
Izomorf/univerzális JavaScript alkalmazások esetén egy URL paraméter és egy variadikus útvonal kombinációja nagyon hasznos az egyéni 404-es hibaoldal megjelenítéséhez.
404 Nem található hiba esetén a szerver egy egyéni oldalt küld vissza a kliensnek. Amikor a Mithril.js betöltődik, átirányítja a klienst az alapértelmezett útvonalra, mert nem tudja, hogy az az útvonal.
m.route(document.body, '/', {
'/': homeComponent,
// [...]
'/:404...': errorPageComponent,
});
Előzmény állapot
Teljes mértékben kihasználhatjuk a mögöttes history.pushState
API-t a felhasználói navigációs élmény javítása érdekében. Például egy alkalmazás "megjegyezheti" egy nagy űrlap állapotát, amikor a felhasználó elnavigál egy oldalról. Így, ha a felhasználó megnyomja a böngészőben a Vissza gombot, az űrlap kitöltve lesz, nem pedig üres.
Például létrehozhatsz egy ilyen űrlapot:
var state = {
term: '',
search: function () {
// save the state for this route
// this is equivalent to `history.replaceState({term: state.term}, null, location.href)`
m.route.set(m.route.get(), null, {
replace: true,
state: { term: state.term },
});
// navigate away
location.href = 'https://google.com/?q=' + state.term;
},
};
var Form = {
oninit: function (vnode) {
state.term = vnode.attrs.term || ''; // populated from the `history.state` property if the user presses the back button
},
view: function () {
return m('form', [
m("input[placeholder='Search']", {
oninput: function (e) {
state.term = e.target.value;
},
value: state.term,
}),
m('button', { onclick: state.search }, 'Search'),
]);
},
};
m.route(document.body, '/', {
'/': Form,
});
Így ha a felhasználó keres, majd a Vissza gombot nyomja meg, a bemenet továbbra is a keresési kifejezéssel lesz kitöltve. Ez a technika javíthatja a nagy űrlapok és más olyan alkalmazások felhasználói élményét, ahol a nem perzisztált állapot előállítása fáradságos a felhasználó számára.
Útválasztó előtagjának módosítása
Az útválasztó előtag az URL azon része, amely meghatározza az útválasztó által használt stratégiát.
// set to pathname strategy
m.route.prefix = '';
// set to querystring strategy
m.route.prefix = '?';
// set to hash without bang
m.route.prefix = '#';
// set to pathname strategy on a non-root URL
// e.g. if the app lives under `https://localhost/my-app` and something else
// lives under `https://localhost`
m.route.prefix = '/my-app';
Haladó komponens megoldás
Ahelyett, hogy egy komponenst egy útvonalhoz rendelnénk, megadhatunk egy RouteResolver
objektumot vagy RouteResolver
-t. Egy RouteResolver
objektum tartalmaz egy onmatch()
és/vagy egy render()
metódust. Mindkét metódus választható, de legalább az egyiknek jelen kell lennie.
m.route(document.body, '/', {
'/': {
onmatch: function (args, requestedPath, route) {
return Home;
},
render: function (vnode) {
return vnode; // egyenértékű a m(Home)-mal
},
},
});
A RouteResolver
-ek hasznosak a különféle haladó útválasztási felhasználási esetek megvalósításához.
Elrendezési komponens körbefoglalása
Gyakran kívánatos az összes vagy a legtöbb útvonalhoz rendelt komponenst egy újrafelhasználható sablonba (gyakran "elrendezésnek" nevezik) csomagolni. Ehhez először létre kell hoznia egy olyan komponenst, amely tartalmazza a közös jelölőnyelvet, amely a különböző komponensek köré kerül:
var Layout = {
view: function (vnode) {
return m('.layout', vnode.children);
},
};
A fenti példában az elrendezés csupán egy <div class="layout">
elemből áll, amely tartalmazza a komponensnek átadott gyermekeket, de a valós életben ez olyan összetett lehet, amennyire csak szükséges.
Az elrendezés körbefoglalásának egyik módja egy névtelen komponens definiálása az útvonalak térképén:
// 1. példa
m.route(document.body, '/', {
'/': {
view: function () {
return m(Layout, m(Home));
},
},
'/form': {
view: function () {
return m(Layout, m(Form));
},
},
});
Azonban vegye figyelembe, hogy mivel a legfelső szintű komponens egy névtelen komponens, a /
útvonalról a /form
útvonalra (vagy fordítva) való ugráskor az lebontásra kerül. Emiatt újra létrehozza a DOM-ot. Ha az Layout
komponensben életciklus metódusok vannak definiálva, az oninit
és az oncreate
hook-ok minden útvonalváltáskor lefutnak. Az alkalmazástól függően ez kívánatos lehet, vagy nem.
Ha azt szeretné, hogy az Layout
komponens a változások alapján frissüljön, és ne legyen minden alkalommal újra létrehozva, akkor ehelyett egy RouteResolver
-t kell használnia gyökér objektumként:
// 2. példa
m.route(document.body, '/', {
'/': {
render: function () {
return m(Layout, m(Home));
},
},
'/form': {
render: function () {
return m(Layout, m(Form));
},
},
});
Vegye figyelembe, hogy ebben az esetben, ha az Layout
komponens rendelkezik oninit
és oncreate
életciklus metódusokkal, akkor azok csak az első útvonalváltáskor futnak le (feltételezve, hogy minden útvonal ugyanazt az elrendezést használja).
A két példa közötti különbség szemléltetése érdekében az 1. példa egyenértékű ezzel a kóddal:
// funkcionálisan egyenértékű az 1. példával
var Anon1 = {
view: function () {
return m(Layout, m(Home));
},
};
var Anon2 = {
view: function () {
return m(Layout, m(Form));
},
};
m.route(document.body, '/', {
'/': {
render: function () {
return m(Anon1);
},
},
'/form': {
render: function () {
return m(Anon2);
},
},
});
Mivel az Anon1
és az Anon2
különböző komponensek, az alágaik (beleértve az Layout
-ot is) a semmiből újra létrejönnek. Ez akkor is megtörténik, ha a komponenseket RouteResolver
nélkül használják közvetlenül.
A 2. példában, mivel az Layout
a legfelső szintű komponens mindkét útvonalon, az Layout
komponens DOM-ja összehasonlításra kerül (azaz sértetlenül marad, ha nincsenek változások), és csak a Home
-ról a Form
-ra való változás váltja ki a DOM ezen szakaszának újbóli létrehozását.
Átirányítás
A RouteResolver
onmatch
hook-ja lehetővé teszi logika futtatását, mielőtt egy útvonal legfelső szintű komponense inicializálódik. Használhatja a Mithril m.route.set()
függvényét vagy a natív HTML history
API-ját. A history
API-val történő átirányításkor az onmatch
hook-nak egy soha be nem teljesülő Promise
-t kell visszaadnia, hogy az útvonalváltás ne fejeződjön be. Az m.route.set()
belsőleg megszakítja a megfeleltetett útvonal feloldását, így ez nem szükséges vele.
Példa: Hitelesítés
Az alábbi példa bemutatja, hogyan lehet megvalósítani egy bejelentkezési falat, amely megakadályozza, hogy a felhasználók láthassák a /secret
oldalt, hacsak nem jelentkeznek be.
var isLoggedIn = false;
var Login = {
view: function () {
return m('form', [
m(
'button[type=button]',
{
onclick: function () {
isLoggedIn = true;
m.route.set('/secret');
},
},
'Login'
),
]);
},
};
m.route(document.body, '/secret', {
'/secret': {
onmatch: function () {
if (!isLoggedIn) m.route.set('/login');
else return Home;
},
},
'/login': Login,
});
Amikor az alkalmazás betöltődik, az onmatch
meghívódik, és mivel az isLoggedIn
hamis, az alkalmazás átirányít a /login
oldalra. Miután a felhasználó megnyomta a bejelentkezés gombot, az isLoggedIn
igaz értékre állítódik, és az alkalmazás átirányít a /secret
oldalra. Az onmatch
hook ismét lefut, és mivel az isLoggedIn
ezúttal igaz, az alkalmazás rendereli a Home
komponenst.
Az egyszerűség kedvéért a fenti példában a felhasználó bejelentkezett állapotát egy globális változóban tároljuk el, és ennek az értéke változik, amikor a felhasználó a bejelentkezés gombra kattint. A valós alkalmazásokban a felhasználónak megfelelő bejelentkezési adatokat kell megadnia. A bejelentkezés gombra kattintva egy kérés indul el a szerver felé a felhasználó hitelesítéséhez.
var Auth = {
username: '',
password: '',
setUsername: function (value) {
Auth.username = value;
},
setPassword: function (value) {
Auth.password = value;
},
login: function () {
m.request({
url: '/api/v1/auth',
params: { username: Auth.username, password: Auth.password },
}).then(function (data) {
localStorage.setItem('auth-token', data.token);
m.route.set('/secret');
});
},
};
var Login = {
view: function () {
return m('form', [
m('input[type=text]', {
oninput: function (e) {
Auth.setUsername(e.target.value);
},
value: Auth.username,
}),
m('input[type=password]', {
oninput: function (e) {
Auth.setPassword(e.target.value);
},
value: Auth.password,
}),
m('button[type=button]', { onclick: Auth.login }, 'Login'),
]);
},
};
m.route(document.body, '/secret', {
'/secret': {
onmatch: function () {
if (!localStorage.getItem('auth-token')) m.route.set('/login');
else return Home;
},
},
'/login': Login,
});
Adatok előtöltése
Jellemzően egy komponens inicializáláskor adatokat tölthet be. Az adatok ilyen módon történő betöltése kétszer rendereli a komponenst. Az első renderelési fázis az útválasztáskor következik be, a második pedig a kérés befejezése után. Fontos megjegyezni, hogy a loadUsers()
egy Promise
-t ad vissza, de az oninit
által visszaadott bármely Promise
jelenleg figyelmen kívül van hagyva. A második renderelési fázis az m.request
background
opciójából származik.
var state = {
users: [],
loadUsers: function () {
return m.request('/api/v1/users').then(function (users) {
state.users = users;
});
},
};
m.route(document.body, '/user/list', {
'/user/list': {
oninit: state.loadUsers,
view: function () {
return state.users.length > 0
? state.users.map(function (user) {
return m('div', user.id);
})
: 'loading';
},
},
});
A fenti példában az első rendereléskor a felhasználói felület a "loading"
szöveget jeleníti meg, mivel a state.users
egy üres tömb, mielőtt a kérés befejeződne. Ezután, amint az adatok elérhetővé válnak, a felhasználói felület újrarajzolódik, és megjelenik a felhasználói azonosítók listája.
A RouteResolver
-ek használhatók az adatok előtöltésére a komponens renderelése előtt. Ezzel elkerülhető a felhasználói felület villogása, és nem lesz szükség betöltési indikátorra.
var state = {
users: [],
loadUsers: function () {
return m.request('/api/v1/users').then(function (users) {
state.users = users;
});
},
};
m.route(document.body, '/user/list', {
'/user/list': {
onmatch: state.loadUsers,
render: function () {
return state.users.map(function (user) {
return m('div', user.id);
});
},
},
});
A fentiek alapján a render
csak a kérés befejezése után fut le, így a feltételes operátor redundáns.
Kód felosztás
Egy nagyméretű alkalmazásban kívánatos lehet az egyes útvonalak kódjának igény szerinti letöltése, ahelyett, hogy az összes kódot előre betöltenénk. A kódbázis ilyen módon történő felosztása kódosztásként vagy késleltetett betöltésként ismert. A Mithril.js-ben ez úgy érhető el, hogy egy Promise
-t adunk vissza az onmatch
hook-ból:
A legegyszerűbb formájában a következőket lehet tenni:
// Home.js
module.export = {
view: function () {
return [m(Menu), m('h1', 'Home')];
},
};
// index.js
function load(file) {
return m.request({
method: 'GET',
url: file,
extract: function (xhr) {
return new Function(
'var module = {};' + xhr.responseText + ';return module.exports;'
);
},
});
}
m.route(document.body, '/', {
'/': {
onmatch: function () {
return load('Home.js');
},
},
});
Azonban a valóságban, ahhoz, hogy ez éles környezetben működjön, a Home.js
modul összes függőségét egyetlen fájlba kell csomagolni. Ezt a fájlt szolgálja ki a szerver.
Szerencsére számos eszköz segíti a modulok késleltetett betöltéshez történő csomagolását. Íme egy példa a natív dinamikus import(...)
használatával, amelyet sok csomagoló támogat:
m.route(document.body, '/', {
'/': {
onmatch: function () {
return import('./Home.js');
},
},
});
Ellenőrzött útvonalak
Bizonyos speciális esetekben szükség lehet az útvonalak paramétereinek szigorúbb ellenőrzésére, például csak numerikus azonosítókat engedélyezni. Ezt meglehetősen egyszerűen megteheti, ha m.route.SKIP
értéket ad vissza egy útvonalról.
m.route(document.body, '/', {
'/view/:id': {
onmatch: function (args) {
if (!/^\d+$/.test(args.id)) return m.route.SKIP;
return ItemView;
},
},
'/view/:name': UserView,
});
Rejtett útvonalak
Például, ha egy felhasználó nem tekinthet meg egy másik felhasználót, nem szeretnénk engedélyezési hibát mutatni. Ehelyett úgy teszünk, mintha az adott útvonal nem létezne, és egy 404-es oldalt jelenítünk meg. Ebben az esetben használhatja az m.route.SKIP
parancsot, hogy úgy tegyen, mintha az útvonal nem létezne.
m.route(document.body, '/', {
'/user/:id': {
onmatch: function (args) {
return Model.checkViewable(args.id).then(function (viewable) {
return viewable ? UserView : m.route.SKIP;
});
},
},
'/:404...': PageNotFound,
});
Útvonal blokkolása
A RouteResolver
onmatch
megakadályozhatja az útvonal betöltését egy soha be nem teljesülő Promise
visszaadásával. Ez felhasználható a redundáns útvonalváltási kísérletek észlelésére és megszakítására:
m.route(document.body, '/', {
'/': {
onmatch: function (args, requestedPath) {
if (m.route.get() === requestedPath) return new Promise(function () {});
},
},
});
Külső integráció
Bizonyos helyzetekben előfordulhat, hogy együtt kell működnie egy másik keretrendszerrel, például a React-tel. Íme, hogyan kell csinálni:
- Definiálja az összes útvonalat az
m.route
használatával a szokásos módon, de győződjön meg arról, hogy csak egyszer használja. Több útvonalpont nem támogatott. - Ha el kell távolítania az útválasztási feliratkozásokat, használja az
m.mount(root, null)
parancsot, ugyanazzal a gyökérelemmel, amelyet azm.route(root, ...)
parancsban használt. Azm.route
belsőleg azm.mount
függvényt használja a működéshez, tehát ez nem varázslat.
Íme egy példa a React-tel:
class Child extends React.Component {
constructor(props) {
super(props);
this.root = React.createRef();
}
componentDidMount() {
m.route(this.root, '/', {
// ...
});
}
componentDidUnmount() {
m.mount(this.root, null);
}
render() {
return <div ref={this.root} />;
}
}
És itt van a durva megfelelője a Vue-val:
<div ref="root"></div>
Vue.component('my-child', {
template: `<div ref="root"></div>`,
mounted: function () {
m.route(this.$refs.root, '/', {
// ...
});
},
destroyed: function () {
m.mount(this.$refs.root, null);
},
});