route(root, defaultRoute, routes)
Popis
Zajišťuje navigaci mezi stránkami v aplikaci.
var Home = {
view: function () {
return 'Welcome';
},
};
m.route(document.body, '/home', {
'/home': Home, // definuje `https://localhost/#!/home`
});
V aplikaci smí být pouze jedno volání m.route
.
Signatura
m.route(root, defaultRoute, routes)
Argument | Type | Required | Description |
---|---|---|---|
root | Element | Ano | DOM element, který bude rodičovským uzlem pro vykreslovaný podstrom. |
defaultRoute | String | Ano | Cesta, na kterou se má přesměrovat, pokud aktuální URL neodpovídá žádné definované cestě. Poznámka: Toto není počáteční cesta aplikace. Počáteční cesta je URL v adresním řádku prohlížeče. |
routes | Object<String,Component|RouteResolver> | Ano | Objekt, jehož klíče jsou cesty a hodnoty jsou buď komponenty, nebo RouteResolver. |
returns | Vrací undefined . |
Statické členy
m.route.set
Přesměruje na odpovídající cestu, nebo na výchozí cestu, pokud nelze najít žádnou odpovídající cestu. Spustí asynchronní překreslení všech připojených komponent.
m.route.set(path, params, options)
Argument | Type | Required | Description |
---|---|---|---|
path | String | Ano | Název cesty, na kterou se má přesměrovat, bez předpony. Cesta může obsahovat parametry, které budou interpolovány s hodnotami z params . |
params | Object | Ne | Parametry směrování. Pokud path obsahuje zástupné symboly pro parametry, vlastnosti tohoto objektu se interpolují do řetězce cesty. |
options.replace | Boolean | Ne | Určuje, zda se má vytvořit nový záznam v historii prohlížeče, nebo nahradit aktuální. Výchozí hodnota je false . |
options.state | Object | Ne | Objekt state , který se má předat podkladovému volání history.pushState / history.replaceState . Tento objekt stavu je k dispozici ve vlastnosti history.state a je sloučen do objektu parametrů směrování. Upozorňujeme, že tato možnost funguje pouze při použití rozhraní API pushState , ale je ignorována, pokud prohlížeč toto API nepodporuje. |
options.title | String | Ne | Řetězec title , který se má předat podkladovému volání history.pushState / history.replaceState . |
returns | Vrací undefined . |
Pamatujte, že při použití .set
s params
musíte také definovat cestu s parametry:
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
Vrací poslední plně vyřešenou cestu směrování, bez předpony. Může se lišit od cesty zobrazené v adresním řádku, dokud asynchronní cesta čeká na vyřešení.
path = m.route.get()
Argument | Type | Required | Description |
---|---|---|---|
returns | String | Vrací poslední plně vyřešenou cestu. |
m.route.prefix
Definuje předponu směrovače. Předpona směrovače je fragment URL, který určuje podkladovou strategii používanou směrovačem.
m.route.prefix = prefix
Argument | Type | Required | Description |
---|---|---|---|
prefix | String | Ano | Předpona, která řídí podkladovou strategii směrování používanou Mithril.js. |
Toto je jednoduchá vlastnost, takže ji můžete číst i zapisovat.
m.route.Link
Tato komponenta vytváří dynamický směrovaný odkaz. Jejím účelem je vytvářet odkazy <a>
s lokálními href
, které jsou transformovány tak, aby správně fungovaly s předponou cesty.
m(m.route.Link, { href: '/foo' }, 'foo');
// Pokud se m.route.prefix nezměnila z výchozí strategie, vykreslí se:
// <a href="#!/foo">foo</a>
Odkazy akceptují následující speciální atributy:
selector
je to, co by se předalo jako první argument dom
: platný je jakýkoli selektor, včetně prvků, které nejsou<a>
.params
&options
jsou argumenty se stejnými názvy, jaké jsou definovány vm.route.set
.disabled
, pokud jetrue
, zakáže chování směrování a jakýkoli vázaný obslužný programonclick
a připojí atributdata-disabled="true"
pro usnadnění přístupu; pokud je prvek<a>
,href
se odebere.
Chování směrování nelze zabránit pomocí standardních obslužných funkcí událostí: použijte místo toho disabled
.
m(
m.route.Link,
{
href: '/foo',
selector: 'button.large',
disabled: true,
params: { key: 'value' },
options: { replace: true },
},
'link name'
);
// Vykreslí se:
// <button disabled aria-disabled="true" class="large">link name</button>
vnode = m(m.route.Link, attributes, children)
Argument | Type | Required | Description |
---|---|---|---|
attributes.href | Object | Ano | Cílová cesta, na kterou se má navigovat. |
attributes.disabled | Boolean | Ne | Znemožní interakci s prvkem. |
attributes.selector | String|Object|Function | Ne | Selektor pro m , výchozí hodnota je "a" . |
attributes.options | Object | Ne | Nastaví options předané do m.route.set . |
attributes.params | Object | Ne | Nastaví params předané do m.route.set . |
attributes | Object | Ne | Jakékoli další atributy, které mají být předány do m . |
children | Array<Vnode>|String|Number|Boolean | Ne | Podřízené vnode pro tento odkaz. |
returns | Vnode | Vnode. |
m.route.param
Načte parametr cesty z aktuální cesty. Parametr cesty je pár klíč-hodnota. Parametry cesty mohou pocházet z několika různých zdrojů:
- interpolace cest (např. pokud je cesta
/users/:id
a vyřeší se na/users/1
, parametr cesty má klíčid
a hodnotu"1"
) - řetězce dotazů (např. pokud je cesta
/users?page=1
, parametr cesty má klíčpage
a hodnotu"1"
) history.state
(např. pokud jehistory.state
{foo: "bar"}
, parametr cesty má klíčfoo
a hodnotu"bar"
)
value = m.route.param(key)
Argument | Type | Required | Description |
---|---|---|---|
key | String | Ne | Název parametru cesty (např. id v cestě /users/:id nebo page v cestě /users/1?page=3 nebo klíč v history.state ). |
returns | String|Object | Vrací hodnotu pro zadaný klíč. Pokud není zadán klíč, vrátí objekt, který obsahuje všechny klíče interpolace. |
Všimněte si, že ve funkci onmatch
RouteResolveru nebyla nová cesta ještě plně vyřešena a m.route.param()
vrátí parametry předchozí cesty, pokud existují. onmatch
obdrží parametry nové cesty jako argument.
m.route.SKIP
Speciální hodnota, kterou lze vrátit z funkce onmatch
řešiče cest pro přeskočení na další cestu.
RouteResolver
RouteResolver
je objekt, který neslouží jako komponenta a obsahuje metodu onmatch
a/nebo metodu render
. Obě metody jsou volitelné, ale musí být přítomna alespoň jedna.
Pokud lze objekt detekovat jako komponentu (přítomností metody view
nebo tím, že je function
/class
), bude s ním zacházeno jako s komponentou, i když má metody onmatch
nebo render
. Protože RouteResolver
není komponenta, nemá metody životního cyklu.
Obecně platí, že RouteResolvers
by měly být ve stejném souboru jako volání m.route
, zatímco definice komponent by měly být ve svých vlastních modulech.
routeResolver = {onmatch, render}
Při použití komponent si je můžete představit jako speciální syntaxi pro tento řešič cest, za předpokladu, že vaše komponenta je Home
:
var routeResolver = {
onmatch: function () {
return Home;
},
render: function (vnode) {
return [vnode];
},
};
routeResolver.onmatch
Hook onmatch
se volá, když směrovač potřebuje vybrat komponentu, která se má vykreslit. Volá se jednou při každé změně cesty, ale ne při následných překresleních. Lze jej použít ke spuštění logiky před inicializací komponenty (například logika ověřování, předběžné načítání dat, sledování analytiky přesměrování atd.).
Tato metoda také umožňuje asynchronně definovat, která komponenta bude vykreslena, takže je vhodná pro rozdělení kódu a asynchronní načítání modulů. Chcete-li vykreslit komponentu asynchronně, vraťte Promise
, který se vyřeší na komponentu.
Další informace o onmatch
naleznete v části rozšířené řešení komponent.
routeResolver.onmatch(args, requestedPath, route)
Argument | Type | Description |
---|---|---|
args | Object | Parametry směrování. |
requestedPath | String | Cesta směrovače požadovaná poslední akcí směrování, včetně interpolovaných hodnot parametrů směrování, ale bez předpony. Když je volána funkce onmatch , řešení pro tuto cestu není dokončeno a m.route.get() stále vrací předchozí cestu. |
route | String | Cesta směrovače požadovaná poslední akcí směrování, s vyloučením interpolovaných hodnot parametrů směrování. |
returns | Component|\Promise<Component>|undefined | Vrací komponentu nebo Promise , který se vyřeší na komponentu. |
Pokud onmatch
vrátí komponentu nebo Promise
, který se vyřeší na komponentu, použije se tato komponenta jako vnode.tag
pro první argument v metodě render
RouteResolveru. V opačném případě je vnode.tag
nastaven na "div"
. Podobně, pokud je metoda onmatch
vynechána, vnode.tag
je také "div"
.
Pokud onmatch
vrátí Promise
, který je odmítnut, směrovač se přesměruje zpět na defaultRoute
. Toto chování můžete přepsat voláním .catch
na řetězci Promise
před jeho vrácením.
routeResolver.render
Metoda render
se volá při každém překreslení pro odpovídající cestu. Je podobná metodě view
v komponentách a existuje pro zjednodušení skládání komponent. Umožňuje vám také uniknout z normálního chování Mithril.js, které nahrazuje celý podstrom.
vnode = routeResolver.render(vnode)
Argument | Type | Description |
---|---|---|
vnode | Object | Vnode, jehož objekt atributů obsahuje parametry směrování. Pokud onmatch nevrátí komponentu nebo Promise , který se vyřeší na komponentu, pole tag vnode má výchozí hodnotu "div" . |
vnode.attrs | Object | Mapa hodnot parametrů URL. |
returns | Array<Vnode>|Vnode | Vnodes, které mají být vykresleny. |
Parametr vnode
je pouze m(Component, m.route.param())
, kde Component
je vyřešená komponenta pro cestu (po routeResolver.onmatch
) a m.route.param()
je popsáno zde. Pokud tuto metodu vynecháte, výchozí návratová hodnota je [vnode]
, zabalená ve fragmentu, abyste mohli použít klíčové parametry. V kombinaci s parametrem :key
se stane jednoelementovým fragmentem s klíčem, protože nakonec vykreslí něco jako [m(Component, {key: m.route.param("key"), ...})]
.
Jak to funguje
Směrování je systém, který umožňuje vytvářet Single Page Applications (SPA), tj. aplikace, které mohou přecházet z jedné stránky na druhou, aniž by došlo k úplnému obnovení stránky v prohlížeči.
Umožňuje bezproblémovou navigaci při zachování možnosti přidat každou stránku jednotlivě do záložek a možnosti navigovat v aplikaci prostřednictvím mechanismu historie prohlížeče.
Směrování bez obnovení stránky je částečně umožněno rozhraním API history.pushState
. Pomocí tohoto rozhraní API je možné programově změnit URL zobrazenou prohlížečem po načtení stránky, ale je odpovědností vývojáře aplikace zajistit, aby navigace na danou URL ze studeného stavu (např. nová karta) vykreslila příslušné značky.
Strategie směrování
Strategie směrování určuje, jak knihovna implementuje směrování. Existují tři obecné strategie, které lze použít k implementaci systému směrování SPA, a každá má svá omezení:
m.route.prefix = '#!'
(výchozí) – Použití identifikátoru fragmentu (aka hash) části URL. URL používající tuto strategii obvykle vypadá takto:https://localhost/#!/page1
.m.route.prefix = '?'
– Použití řetězce dotazu. URL používající tuto strategii obvykle vypadá takto:https://localhost/?/page1
.m.route.prefix = ''
– Použití názvu cesty. URL používající tuto strategii obvykle vypadá takto:https://localhost/page1
.
Použití strategie hash zaručeně funguje v prohlížečích, které nepodporují history.pushState
, protože může použít onhashchange
jako náhradu. Použijte tuto strategii, pokud chcete zachovat hashe čistě lokální.
Strategie řetězce dotazu umožňuje detekci na straně serveru, ale nezobrazuje se jako normální cesta. Použijte tuto strategii, pokud chcete podporovat a potenciálně detekovat ukotvené odkazy na straně serveru a nemůžete provést změny nezbytné pro podporu strategie názvu cesty (například pokud používáte Apache a nemůžete upravit svůj .htaccess
).
Strategie názvu cesty vytváří nejčistší URL, ale vyžaduje nastavení serveru tak, aby obsluhoval kód aplikace pro jednu stránku z každé URL, na kterou může aplikace směrovat. Použijte tuto strategii, pokud chcete čistší URL.
Aplikace pro jednu stránku, které používají strategii hash, často používají konvenci mít vykřičník za hashem, aby naznačily, že používají hash jako mechanismus směrování, a ne pro účely odkazování na kotvy. Řetězec #!
je známý jako hashbang.
Výchozí strategie používá hashbang.
Typické použití
Obvykle musíte vytvořit několik komponent pro definování jednotlivých stránek:
var Home = {
view: function () {
return [m(Menu), m('h1', 'Home')];
},
};
var Page1 = {
view: function () {
return [m(Menu), m('h1', 'Page 1')];
},
};
Ve výše uvedeném příkladu existují dvě komponenty: Home
a Page1
. Každá obsahuje menu a nějaký text. Menu je samo o sobě definováno jako komponenta, aby se zabránilo opakování:
var Menu = {
view: function () {
return m('nav', [
m(m.route.Link, { href: '/' }, 'Home'),
m(m.route.Link, { href: '/page1' }, 'Page 1'),
]);
},
};
Nyní můžeme definovat cesty a mapovat na ně naše komponenty:
m.route(document.body, '/', {
'/': Home,
'/page1': Page1,
});
Zde specifikujeme dvě cesty: /
a /page1
, které vykreslují své příslušné komponenty, když uživatel přejde na každou URL.
Navigace na různé cesty
Ve výše uvedeném příkladu má komponenta Menu
dva m.route.Link
s. To vytvoří prvek, ve výchozím nastavení <a>
, a nastaví jej tak, že pokud na něj uživatel klikne, sám přejde na jinou cestu. Neprovádí plnohodnotnou navigaci, pouze lokální změnu v aplikaci.
Můžete také navigovat programově pomocí m.route.set(route)
. Například m.route.set("/page1")
.
Při navigaci mezi cestami je předpona směrovače zpracována za vás. Jinými slovy, vynechte hashbang #!
(nebo jakoukoli předponu, kterou jste nastavili m.route.prefix
na) při propojování cest Mithril.js, a to jak v m.route.set
, tak v m.route.Link
.
Všimněte si, že při navigaci mezi komponentami je nahrazen celý podstrom. Použijte řešič cest s metodou render
, pokud chcete pouze aktualizovat podstrom.
Parametry směrování
Někdy chceme mít v cestě ID nebo jiná data, která se mění, ale nechceme explicitně specifikovat samostatnou cestu pro každé možné ID. Abychom toho dosáhli, Mithril.js podporuje parametrizované cesty:
var Edit = {
view: function (vnode) {
return [m(Menu), m('h1', 'Editing ' + vnode.attrs.id)];
},
};
m.route(document.body, '/edit/1', {
'/edit/:id': Edit,
});
Ve výše uvedeném příkladu jsme definovali cestu /edit/:id
. To vytvoří dynamickou cestu, která odpovídá jakékoli URL, která začíná /edit/
a následují ji nějaká data (např. /edit/1
, edit/234
atd.). Hodnota id
je pak přístupná jako atribut vnode komponenty (vnode.attrs.id
).
Je možné mít v cestě více argumentů, například /edit/:projectID/:userID
by vytvořilo vlastnosti projectID
a userID
v objektu atributů vnode komponenty.
Klíčový parametr
Když uživatel přejde z parametrizované cesty na stejnou cestu s jiným parametrem (např. přechod z /page/1
na /page/2
vzhledem k cestě /page/:id
), komponenta by nebyla znovu vytvořena od začátku, protože obě cesty se vyřeší na stejnou komponentu, a proto by vedly k virtuálnímu dom diff na místě. To způsobí spuštění hooku onupdate
místo oninit
/oncreate
. Je však poměrně běžné, že vývojář chce synchronizovat opětovné vytvoření komponenty s událostí změny cesty.
Abychom toho dosáhli, je možné kombinovat parametrizaci cesty s klíči pro velmi pohodlný vzor:
m.route(document.body, '/edit/1', {
'/edit/:key': Edit,
});
To znamená, že vnode, který je vytvořen pro kořenovou komponentu cesty, má objekt parametru cesty key
. Parametry cesty se stanou attrs
ve vnode. Když tedy skočíte z jedné stránky na druhou, key
se změní a způsobí, že se komponenta znovu vytvoří od začátku (protože klíč říká enginu virtuálního dom, že staré a nové komponenty jsou různé entity).
Můžete tuto myšlenku posunout dále a vytvořit komponenty, které se znovu vytvoří při opětovném načtení:
m.route.set(m.route.get(), {key: Date.now()})
Nebo dokonce použít funkci history state
k dosažení znovu načitatelných komponent bez znečištění URL:
m.route.set(m.route.get(), null, {state: {key: Date.now()}})
Všimněte si, že klíčový parametr funguje pouze pro cesty komponent. Pokud používáte řešič cest, budete muset použít jednoelementový fragment s klíčem a předat key: m.route.param("key")
, abyste dosáhli stejného výsledku.
Variadické cesty
Je také možné mít variadické cesty, tj. cestu s argumentem, který obsahuje URL s lomítky:
m.route(document.body, '/edit/pictures/image.jpg', {
'/edit/:file...': Edit,
});
Zpracování chyb 404
Pro izomorfní / univerzální aplikaci JavaScript je kombinace parametru URL a variadické cesty velmi užitečná pro zobrazení vlastní chybové stránky.
V případě chyby 404 Nenalezeno server odešle zpět vlastní chybovou stránku. Když je načten Mithril.js, přesměruje klienta na výchozí cestu, protože nemůže vědět, že cesta.
m.route(document.body, '/', {
'/': homeComponent,
// [...]
'/:404...': errorPageComponent,
});
Stav historie
Je možné plně využít podkladové rozhraní API history.pushState
ke zlepšení uživatelského prostředí při navigaci. Například aplikace by si mohla "zapamatovat" stav velkého formuláře, když uživatel opustí stránku navigací pryč, takže pokud by uživatel stiskl tlačítko zpět v prohlížeči, měl by vyplněný formulář spíše než prázdný formulář.
Pro příklad, můžete vytvořit formulář takto:
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,
});
Tímto způsobem, pokud uživatel vyhledá a stiskne tlačítko zpět pro návrat do aplikace, bude vstup stále vyplněn hledaným výrazem. Tato technika může zlepšit uživatelský zážitek z velkých formulářů a dalších aplikací, kde je neperzistentní stav pro uživatele pracný.
Změna předpony směrovače
Předpona směrovače je fragment URL, který určuje podkladovou strategii používanou směrovačem.
// nastavit na strategii názvu cesty
m.route.prefix = '';
// nastavit na strategii řetězce dotazu
m.route.prefix = '?';
// nastavit na hash bez bang
m.route.prefix = '#';
// nastavit na strategii názvu cesty na URL, která není kořenová
// např. pokud aplikace žije pod `https://localhost/my-app`
m.route.prefix = '/my-app';
Pokročilé rozlišení komponent
Místo přímého mapování komponenty na cestu můžete použít objekt RouteResolver
. Objekt RouteResolver
obsahuje metody onmatch()
a/nebo render()
. Obě metody jsou volitelné, ale alespoň jedna z nich musí být definována.
m.route(document.body, '/', {
'/': {
onmatch: function (args, requestedPath, route) {
return Home;
},
render: function (vnode) {
return vnode; // ekvivalentní m(Home)
},
},
});
RouteResolver
je užitečný pro implementaci pokročilejších scénářů směrování.
Zabalení layoutové komponenty
Často je potřeba zabalit všechny nebo většinu směrovaných komponent do opakovaně použitelného obalu (často nazývaného "rozvržení"). K tomu je nutné nejprve vytvořit komponentu, která obsahuje společné prvky, jež budou obalovat různé komponenty:
var Layout = {
view: function (vnode) {
return m('.layout', vnode.children);
},
};
V uvedeném příkladu se rozvržení skládá pouze z <div>
s třídou "layout", která obsahuje potomky předané komponentě. V reálném scénáři může být rozvržení libovolně složité.
Jedním ze způsobů, jak zabalit rozvržení, je definovat anonymní komponentu přímo v mapě cest:
// příklad 1
m.route(document.body, '/', {
'/': {
view: function () {
return m(Layout, m(Home));
},
},
'/form': {
view: function () {
return m(Layout, m(Form));
},
},
});
Všimněte si, že komponenta nejvyšší úrovně je anonymní. Při přechodu z cesty /
na cestu /form
(nebo naopak) se anonymní komponenta zruší a DOM se znovu vytvoří od začátku. Pokud má komponenta Layout
definované metody životního cyklu, háčky oninit
a oncreate
se spustí při každé změně cesty. V závislosti na aplikaci to může být žádoucí, nebo ne.
Pokud chcete, aby byla komponenta Layout
porovnávána a udržována v původním stavu, namísto opětovného vytváření od začátku, použijte místo toho RouteResolver
jako kořenový objekt:
// příklad 2
m.route(document.body, '/', {
'/': {
render: function () {
return m(Layout, m(Home));
},
},
'/form': {
render: function () {
return m(Layout, m(Form));
},
},
});
V tomto případě, pokud má komponenta Layout
metody životního cyklu oninit
a oncreate
, spustí se pouze při první změně cesty (za předpokladu, že všechny cesty používají stejné rozvržení).
Pro objasnění rozdílu mezi oběma příklady je příklad 1 ekvivalentní tomuto kódu:
// funkčně ekvivalentní příkladu 1
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);
},
},
});
Protože Anon1
a Anon2
jsou odlišné komponenty, jejich podstromy (včetně Layout
) jsou znovu vytvořeny od začátku. To se stane i v případě, že jsou komponenty použity přímo bez RouteResolver
u.
V příkladu 2, protože Layout
je nejvyšší komponenta v obou cestách, DOM pro komponentu Layout
je porovnán (tj. ponechán beze změny, pokud nemá žádné změny) a pouze změna z Home
na Form
spustí opětovné vytvoření této části DOM.
Přesměrování (redirect)
Háček onmatch
v RouteResolver
umožňuje spuštění logiky před inicializací komponenty nejvyšší úrovně dané cesty. Můžete použít buď Mithrilovu funkci m.route.set()
nebo nativní HTML API history
. Při přesměrování pomocí API history
je nutné, aby háček onmatch
vrátil Promise, která se nikdy nevyřeší, a tím zabránil vyřešení odpovídající cesty. m.route.set()
interně ruší vyřešení odpovídající cesty, takže to není nutné.
Příklad: autentifikace
Následující příklad ukazuje, jak vytvořit přihlašovací stěnu, která uživatelům zabrání v zobrazení stránky /secret
, pokud se nepřihlásí.
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,
});
Při načtení aplikace se zavolá onmatch
, a protože isLoggedIn
je false
, aplikace se přesměruje na /login
. Po stisknutí tlačítka pro přihlášení se hodnota isLoggedIn
změní na true
a aplikace se přesměruje na /secret
. Háček onmatch
se spustí znovu, a protože isLoggedIn
je tentokrát true
, aplikace vykreslí komponentu Home
.
Pro zjednodušení je stav přihlášení uživatele uložen v globální proměnné. Ve výše uvedeném příkladu... a tento příznak se pouze přepne, když uživatel klikne na tlačítko pro přihlášení. Ve skutečné aplikaci by uživatel samozřejmě musel zadat správné přihlašovací údaje a kliknutí na tlačítko pro přihlášení by spustilo požadavek na server pro ověření uživatele:
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,
});
Přednačítání dat
Komponenta obvykle načítá data při inicializaci. Načítání dat tímto způsobem způsobuje, že se komponenta vykreslí dvakrát. První průchod vykreslením nastane při směrování a druhý se spustí po dokončení požadavku. Všimněte si, že loadUsers()
vrací promise, ale jakýkoli Promise vrácený oninit
je aktuálně ignorován. Druhý průchod vykreslením pochází z možnosti background
pro m.request
.
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';
},
},
});
V uvedeném příkladu se při prvním vykreslení uživatelského rozhraní zobrazí "loading"
, protože state.users
je prázdné pole před dokončením požadavku. Jakmile jsou data k dispozici, uživatelské rozhraní se překreslí a zobrazí se seznam ID uživatelů.
RouteResolver
lze využít k předběžnému načtení dat před vykreslením komponenty, čímž se předejde blikání uživatelského rozhraní a eliminuje potřeba indikátoru načítání:
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);
});
},
},
});
render
se spustí až po dokončení požadavku, takže ternární operátor je nadbytečný.
Dělení kódu
Ve velkých aplikacích může být žádoucí stahovat kód pro každou cestu na vyžádání, spíše než předem. Tento způsob rozdělení kódu je známý jako code-splitting nebo líné načítání. V Mithril.js toho lze dosáhnout vrácením promise z háčku onmatch
:
V nejzákladnější podobě by to mohlo vypadat takto:
// 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');
},
},
});
V reálném provozu by však bylo nutné svázat všechny závislosti pro modul Home.js
do souboru, který je nakonec obsluhován serverem.
Naštěstí existuje řada nástrojů, které usnadňují svazování modulů pro líné načítání. Zde je příklad použití nativního dynamického import(...)
, podporovaného mnoha bundlery:
m.route(document.body, '/', {
'/': {
onmatch: function () {
return import('./Home.js');
},
},
});
Typované routy
V některých složitějších případech směrování může být žádoucí omezit hodnotu nejen na samotnou cestu, například tak, aby odpovídala pouze číselnému ID. Můžete to snadno provést vrácením m.route.SKIP
z cesty.
m.route(document.body, '/', {
'/view/:id': {
onmatch: function (args) {
if (!/^\d+$/.test(args.id)) return m.route.SKIP;
return ItemView;
},
},
'/view/:name': UserView,
});
Skryté routy
Ve vzácných případech můžete chtít skrýt určité cesty před některými uživateli, ale ne pro všechny. Například uživatel může mít zakázáno zobrazit konkrétního uživatele, a místo zobrazení chyby oprávnění byste raději předstírali, že neexistuje, a přesměrovali na zobrazení 404. V takovém případě můžete použít m.route.SKIP
, abyste předstírali, že cesta neexistuje.
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,
});
Zrušení / blokování cesty
RouteResolver
onmatch
může zabránit vyřešení cesty vrácením promise, která se nikdy nevyřeší. To lze použít k detekci pokusů o nadbytečné vyřešení cesty a jejich zrušení:
m.route(document.body, '/', {
'/': {
onmatch: function (args, requestedPath) {
if (m.route.get() === requestedPath) return new Promise(function () {});
},
},
});
Integrace s třetími stranami
V určitých situacích můžete potřebovat spolupracovat s jiným frameworkem, jako je React. Zde je postup, jak to udělat:
- Definujte všechny své cesty pomocí
m.route
, jak jste zvyklí, ale ujistěte se, že jej používáte pouze jednou. Použití více směrovacích bodů není podporováno. - Když potřebujete zrušit odběry směrování, použijte
m.mount(root, null)
, pomocí stejného kořene, který jste použilim.route(root, ...)
na.m.route
používá interněm.mount
k připojení všeho, takže to není magie.
Zde je příklad použití Reactu:
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} />;
}
}
A zde je přibližný ekvivalent ve Vue:
<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);
},
});