Skip to content
Mithril.js 2
Main Navigation AnleitungAPI

Deutsch

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Deutsch

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Aussehen

Sidebar Navigation

API

Kern-API

m(selector, attributes, children)

render(element, vnodes)

mount(root, component)

route(root, defaultRoute, routes)

request(options)

parseQueryString(string)

buildQueryString(object)

buildPathname(object)

parsePathname(string)

trust(html)

fragment(attrs, children)

redraw()

censor(object, extra)

Optionale API

stream()

Anleitung

Auf dieser Seite

route(root, defaultRoute, routes) ​

Beschreibung ​

Ermöglicht das Wechseln zwischen "Seiten" innerhalb einer Single-Page-Anwendung (SPA).

javascript
var Home = {
  view: function () {
    return 'Welcome';
  },
};

m.route(document.body, '/home', {
  '/home': Home, // definiert `https://localhost/#!/home`
});

Pro Anwendung darf nur ein m.route-Aufruf erfolgen.

Signatur ​

m.route(root, defaultRoute, routes)

ArgumentTypErforderlichBeschreibung
rootElementJaEin DOM-Element, das als Elternknoten für den von Mithril verwalteten Teilbaum dient.
defaultRouteStringJaDie Route, zu der weitergeleitet wird, wenn die aktuelle URL keiner definierten Route entspricht. Beachte: Dies ist nicht die anfängliche Route. Die anfängliche Route wird durch die URL in der Adressleiste des Browsers bestimmt.
routesObject<String,Component|RouteResolver>JaEin Objekt, dessen Schlüssel Routen-Strings sind und dessen Werte entweder Komponenten oder ein RouteResolver sind.
returnsGibt undefined zurück.

Wie man Signaturen liest

Statische Elemente ​

m.route.set ​

Leitet zu einer passenden Route um oder zur Standardroute, falls keine passende Route gefunden wird. Löst ein asynchrones Neuzeichnen aller Mount Points aus.

m.route.set(path, params, options)

ArgumentTypErforderlichBeschreibung
pathStringJaDer Pfadname der Zielroute ohne Präfix. Der Pfad kann Parameter enthalten, die mit Werten aus params interpoliert werden.
paramsObjectNeinRouting-Parameter. Falls path Platzhalter für Parameter enthält, werden die Eigenschaften dieses Objekts in den Pfad-String eingefügt.
options.replaceBooleanNeinLegt fest, ob ein neuer Eintrag in der Browser-History erstellt oder der aktuelle Eintrag ersetzt wird. Standard: false.
options.stateObjectNeinDas state-Objekt für den zugrunde liegenden Aufruf von history.pushState / history.replaceState. Dieses State-Objekt wird in der history.state-Eigenschaft verfügbar und mit den Routing-Parametern zusammengeführt. Beachte, dass diese Option nur mit der PushState-API funktioniert und ignoriert wird, falls der Router in den Hashchange-Modus wechselt (z. B. wenn die PushState-API nicht verfügbar ist).
options.titleStringNeinDer title-String für den zugrunde liegenden Aufruf von history.pushState / history.replaceState.
returnsGibt undefined zurück.

Beachte: Wenn .set zusammen mit params verwendet wird, muss die Route ebenfalls definiert sein:

javascript
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 ​

Liefert den zuletzt vollständig aufgelösten Routing-Pfad ohne Präfix. Dieser Pfad kann von dem in der Adressleiste angezeigten Pfad abweichen, solange eine asynchrone Route noch auf die Auflösung wartet.

path = m.route.get()

ArgumentTypErforderlichBeschreibung
returnsStringGibt den letzten vollständig aufgelösten Pfad zurück.

m.route.prefix ​

Legt ein Router-Präfix fest. Das Router-Präfix ist ein Teil der URL, der die zugrunde liegende Strategie des Routers bestimmt.

m.route.prefix = prefix

ArgumentTypErforderlichBeschreibung
prefixStringJaDas Präfix, das die zugrunde liegende Routing-Strategie steuert, die von Mithril verwendet wird.

Da es sich um eine einfache Eigenschaft handelt, kann sie sowohl gelesen als auch geschrieben werden.

m.route.Link ​

Diese Komponente erzeugt einen dynamischen, gerouteten Link. Sie generiert hauptsächlich a-Links mit lokalen href-Attributen, die das Route-Präfix berücksichtigen.

javascript
m(m.route.Link, { href: '/foo' }, 'foo');

// Sofern sich m.route.prefix nicht von der Standardstrategie geändert hat, wird Folgendes gerendert:
// <a href="#!/foo">foo</a>

Links unterstützen folgende spezielle Attribute:

  • selector entspricht dem ersten Argument für m: Beliebige Selektoren sind gültig, auch Nicht-a-Elemente.
  • params und options entsprechen den gleichnamigen Argumenten in m.route.set.
  • disabled: Bei true wird das Routing-Verhalten und gebundene onclick-Handler deaktiviert sowie ein data-disabled="true-Attribut für Barrierefreiheit hinzugefügt; bei a-Elementen wird href entfernt.

Das Routing-Verhalten lässt sich nicht über die Event-Handling-API unterbinden – verwenden Sie stattdessen disabled.

javascript
m(
  m.route.Link,
  {
    href: '/foo',
    selector: 'button.large',
    disabled: true,
    params: { key: 'value' },
    options: { replace: true },
  },
  'link name'
);

// Rendert zu:
// <button disabled aria-disabled="true" class="large">link name</button>

vnode = m(m.route.Link, attributes, children)

ArgumentTypErforderlichBeschreibung
attributes.hrefObjectJaDie Zielroute, zu der navigiert werden soll.
attributes.disabledBooleanNeinDeaktiviert das Element barrierefrei.
attributes.selectorString|Object|FunctionNeinEin Selektor für m, Standardwert ist "a".
attributes.optionsObjectNeinSetzt die options, die an m.route.set übergeben werden.
attributes.paramsObjectNeinSetzt die params, die an m.route.set übergeben werden.
attributesObjectNeinAlle anderen Attribute, die an m weitergeleitet werden sollen.
childrenArray<Vnode>|String|Number|BooleanNeinChild vnodes für diesen Link.
returnsVnodeEin vnode.

m.route.param ​

Ermittelt einen Route-Parameter der zuletzt vollständig aufgelösten Route. Ein Routenparameter ist ein Schlüssel-Wert-Paar und kann aus verschiedenen Quellen stammen:

  • Route-Interpolationen (z. B. bei der Route /users/:id, die zu /users/1 aufgelöst wird, hat der Parameter den Schlüssel id und den Wert "1")
  • Router-Querystrings (z. B. beim Pfad /users?page=1 hat der Parameter den Schlüssel page und den Wert "1")
  • history.state (z. B. bei history.state mit {foo: "bar"} hat der Parameter den Schlüssel foo und den Wert "bar")

value = m.route.param(key)

ArgumentTypErforderlichBeschreibung
keyStringNeinEin Routen-Parameter-Name (z. B. id in Route /users/:id oder page in Pfad /users/1?page=3 oder ein Key in history.state)
returnsString|ObjectLiefert den Wert zum angegebenen Schlüssel. Ohne Schlüsselangabe wird ein Objekt mit allen Interpolations-Schlüsseln zurückgegeben.

Hinweis: In der onmatch-Funktion eines RouteResolvers ist die neue Route noch nicht vollständig aufgelöst, sodass m.route.param() die Parameter der vorherigen Route (falls vorhanden) liefert. onmatch erhält die neuen Route-Parameter als Argument.

m.route.SKIP ​

Ein spezieller Wert, der von der onmatch-Funktion eines Route Resolvers zurückgegeben werden kann, um zur nächsten passenden Route zu springen.

RouteResolver ​

Ein Route-Resolver ist ein Objekt ohne Komponentenstatus, das eine onmatch- und/oder render-Methode enthält. Beide Methoden sind optional, aber mindestens eine muss vorhanden sein.

Falls ein Objekt als Komponente erkennbar ist (durch eine view-Methode oder als function/class), wird es auch mit onmatch- oder render-Methoden als Komponente behandelt. Da ein Route-Resolver keine Komponente ist, besitzt er keine Lifecycle-Methoden.

Als Faustregel gilt: RouteResolver sollten sich in derselben Datei wie der m.route-Aufruf befinden, während Komponentendefinitionen in eigenen Modulen stehen sollten.

routeResolver = {onmatch, render}

Bei Verwendung von Komponenten kann man sich diese als eine Art "syntaktischen Zucker" für diesen Route Resolver vorstellen, vorausgesetzt, die Komponente ist Home:

javascript
var routeResolver = {
  onmatch: function () {
    return Home;
  },
  render: function (vnode) {
    return [vnode];
  },
};

routeResolver.onmatch ​

Die onmatch-Funktion wird aufgerufen, wenn der Router eine zu rendernde Komponente ermitteln muss. Sie wird einmal pro Router-Pfadänderung aufgerufen, aber nicht bei nachfolgenden Redraws auf demselben Pfad. Sie kann verwendet werden, um Logik auszuführen, bevor eine Komponente initialisiert wird (z. B. Authentifizierungslogik, Daten-Preloading, Redirection Analytics Tracking usw.).

Diese Methode ermöglicht es dir auch, asynchron zu definieren, welche Komponente gerendert wird, was sie für Code-Splitting und asynchrones Modul-Laden geeignet macht. Um eine Komponente asynchron zu rendern, gib ein Promise zurück, das zu einer Komponente aufgelöst wird.

Weitere Informationen zu onmatch findest du im Abschnitt Erweiterte Komponentenauflösung

routeResolver.onmatch(args, requestedPath, route)

ArgumentTypBeschreibung
argsObjectDie Routing-Parameter
requestedPathStringDer Router-Pfad, der von der letzten Routing-Aktion angefordert wurde, einschließlich interpolierter Routing-Parameter-Values, aber ohne das Präfix. Wenn onmatch aufgerufen wird, ist die Auflösung für diesen Pfad noch nicht abgeschlossen und m.route.get() gibt noch den vorherigen Pfad zurück.
routeStringDer Router-Pfad, der von der letzten Routing-Aktion angefordert wurde, ohne interpolierte Routing-Parameter-Values
returnsComponent|\Promise<Component>|undefinedGibt eine Komponente oder ein Promise zurück, das zu einer Komponente aufgelöst wird.

Wenn onmatch eine Komponente oder ein Promise zurückgibt, das zu einer Komponente aufgelöst wird, wird diese Komponente als vnode.tag für das erste Argument in der render-Methode des RouteResolvers verwendet. Andernfalls wird vnode.tag auf "div" gesetzt. Wenn die onmatch-Methode weggelassen wird, ist vnode.tag ebenfalls "div".

Wenn onmatch ein Promise zurückgibt, das abgelehnt wird, leitet der Router zurück zu defaultRoute. Du kannst dieses Verhalten überschreiben, indem du .catch in der Promise-Kette aufrufst, bevor du sie zurückgibst.

routeResolver.render ​

Die render-Methode wird bei jedem Redraw für eine passende Route aufgerufen. Sie ähnelt der view-Methode in Komponenten und existiert, um die Komponentenzusammensetzung zu vereinfachen. Sie ermöglicht es auch, vom Standardverhalten von Mithril.js abzuweichen, bei dem der gesamte Subtree ersetzt wird.

vnode = routeResolver.render(vnode)

ArgumentTypBeschreibung
vnodeObjectEin vnode, dessen Attribute-Objekt Routing-Parameter enthält. Wenn onmatch keine Komponente oder ein Promise zurückgibt, das zu einer Komponente aufgelöst wird, ist das tag-Feld des vnodes standardmäßig "div"
vnode.attrsObjectEine Map von URL-Parameter-Values
returnsArray<Vnode>|VnodeDie vnodes, die gerendert werden sollen.

Der vnode-Parameter entspricht m(Component, m.route.param()), wobei Component die aufgelöste Komponente für die Route ist (nach routeResolver.onmatch) und m.route.param() wie hier dokumentiert ist. Wenn du diese Methode weglässt, ist der Standard-Return-Value [vnode], verpackt in einem Fragment, sodass du Key-Parameter verwenden kannst. In Kombination mit einem :key-Parameter wird es zu einem Single-Element Keyed Fragment, da es am Ende zu so etwas wie [m(Component, {key: m.route.param("key"), ...})] gerendert wird.

Wie es funktioniert ​

Routing ist ein System, das die Erstellung von Single-Page-Applications (SPA) ermöglicht, d. h. Anwendungen, die von einer "Seite" zu einer anderen wechseln können, ohne eine vollständige Browser-Aktualisierung zu verursachen.

Es ermöglicht eine nahtlose Navigation und bewahrt gleichzeitig die Möglichkeit, jede Seite einzeln mit einem Lesezeichen zu versehen, sowie die Möglichkeit, die Anwendung über den History-Mechanismus des Browsers zu navigieren.

Routing ohne Seitenaktualisierungen wird teilweise durch die history.pushState API ermöglicht. Mit dieser API ist es möglich, die vom Browser angezeigte URL nach dem Laden einer Seite programmgesteuert zu ändern. Es liegt jedoch in der Verantwortung des Anwendungsentwicklers, sicherzustellen, dass das Navigieren zu einer bestimmten URL aus einem kalten Zustand (z. B. einem neuen Tab) das entsprechende Markup rendert.

Routing-Strategien ​

Die Routingstrategie bestimmt, wie eine Bibliothek das Routing tatsächlich implementiert. Es gibt drei allgemeine Strategien, die verwendet werden können, um ein SPA-Routing-System zu implementieren, und jede hat unterschiedliche Einschränkungen:

  • m.route.prefix = '#!' (Standard) – Verwenden des Fragment Identifier (auch bekannt als Hash)-Teils der URL. Eine URL, die diese Strategie verwendet, sieht typischerweise so aus: https://localhost/#!/page1
  • m.route.prefix = '?' – Verwenden des Querystrings. Eine URL, die diese Strategie verwendet, sieht typischerweise so aus: https://localhost/?/page1
  • m.route.prefix = '' – Verwenden des Pfadnamens. Eine URL, die diese Strategie verwendet, sieht typischerweise so aus: https://localhost/page1

Die Hash-Strategie funktioniert garantiert in Browsern, die history.pushState nicht unterstützen, da sie auf onhashchange zurückgreifen kann. Verwende diese Strategie, wenn du die Hashes rein lokal halten möchtest.

Die Querystring-Strategie ermöglicht die Erkennung auf Serverseite, wird aber nicht als normaler Pfad angezeigt. Verwenden Sie diese Strategie, wenn Sie verankerte Links serverseitig unterstützen und potenziell erkennen möchten, und Sie nicht in der Lage sind, die erforderlichen Änderungen zur Unterstützung der Pfadnamenstrategie vorzunehmen (z. B. wenn Sie Apache verwenden und Ihre .htaccess-Datei nicht ändern können).

Die Pfadnamenstrategie erzeugt die klarsten URLs, erfordert aber, dass der Server so eingerichtet ist, dass er den Single-Page-Application-Code von jeder URL aus bereitstellt, zu der die Anwendung routen kann. Verwende diese Strategie, wenn du sauberer aussehende URLs möchtest.

Single-Page-Applications, die die Hash-Strategie verwenden, verwenden oft die Konvention, ein Ausrufezeichen nach dem Hash zu setzen, um anzuzeigen, dass sie den Hash als Routing-Mechanismus verwenden und nicht zum Verlinken mit Ankern. Der #!-String wird als Hashbang bezeichnet.

Die Standardstrategie nutzt den Hashbang.

Typische Verwendung ​

Normalerweise musst du ein paar Komponenten erstellen, um Routen zuzuordnen:

javascript
var Home = {
  view: function () {
    return [m(Menu), m('h1', 'Home')];
  },
};

var Page1 = {
  view: function () {
    return [m(Menu), m('h1', 'Page 1')];
  },
};

Im obigen Beispiel gibt es zwei Komponenten: Home und Page1. Jede enthält ein Menü und etwas Text. Das Menü selbst wird als Komponente definiert, um Wiederholungen zu vermeiden:

javascript
var Menu = {
  view: function () {
    return m('nav', [
      m(m.route.Link, { href: '/' }, 'Home'),
      m(m.route.Link, { href: '/page1' }, 'Page 1'),
    ]);
  },
};

Jetzt können wir Routen definieren und unsere Komponenten ihnen zuordnen:

javascript
m.route(document.body, '/', {
  '/': Home,
  '/page1': Page1,
});

Hier geben wir zwei Routen an: / und /page1, die ihre jeweiligen Komponenten rendern, wenn der Benutzer zu jeder URL navigiert.

Navigieren zu verschiedenen Routen ​

Im obigen Beispiel enthält die Menu-Komponente zwei m.route.Link-Elemente. Dadurch wird ein Element erstellt, standardmäßig ein <a>-Element, das so konfiguriert ist, dass es bei einem Klick des Benutzers zu einer anderen Route navigiert. Die Navigation erfolgt nicht zu einer externen Seite, sondern innerhalb der Anwendung.

Du kannst auch programmgesteuert navigieren, über m.route.set(route). Zum Beispiel m.route.set("/page1").

Beim Navigieren zwischen Routen wird das Router-Präfix für dich behandelt. Mit anderen Worten, lass den Hashbang #! (oder welches Präfix du auch immer m.route.prefix gesetzt hast) weg, wenn du Mithril.js-Routen verlinkst, sowohl in m.route.set als auch in m.route.Link.

Beachte, dass beim Navigieren zwischen Komponenten der gesamte Subtree ersetzt wird. Verwende einen Route Resolver mit einer render-Methode, wenn du nur den Subtree patchen möchtest.

Routing-Parameter ​

Manchmal möchten wir eine variable ID oder ähnliche Daten in einer Route haben, aber wir möchten nicht explizit für jede mögliche ID eine separate Route angeben. Um dies zu erreichen, unterstützt Mithril.js parametrisierte Routen.

javascript
var Edit = {
  view: function (vnode) {
    return [m(Menu), m('h1', 'Editing ' + vnode.attrs.id)];
  },
};
m.route(document.body, '/edit/1', {
  '/edit/:id': Edit,
});

Im obigen Beispiel haben wir eine Route /edit/:id definiert. Dies erstellt eine dynamische Route, die mit jeder URL übereinstimmt, die mit /edit/ beginnt und von einigen Daten gefolgt wird (z. B. /edit/1, edit/234 usw.). Der Wert von id wird dann als Attribut des vnodes der Komponente zugeordnet (vnode.attrs.id)

Es ist möglich, mehrere Argumente in einer Route zu haben, z. B. /edit/:projectID/:userID würde die Eigenschaften projectID und userID im Attribute-Objekt des vnodes der Komponente ergeben.

Key-Parameter ​

Wenn ein Benutzer von einer parametrisierten Route zur selben Route mit einem anderen Parameter navigiert (z. B. von /page/1 zu /page/2 bei einer Route /page/:id), wird die Komponente nicht von Grund auf neu erstellt, da beide Routen zur selben Komponente aufgelöst werden und somit zu einem In-Place-Diff des virtuellen DOMs führen. Dies hat den Nebeneffekt, dass der onupdate-Hook anstelle von oninit/oncreate ausgelöst wird. Es ist jedoch relativ üblich, dass ein Entwickler die Neuerstellung der Komponente mit dem Routenänderungsereignis synchronisieren möchte.

Um dies zu erreichen, ist es möglich, die Routenparametrisierung mit Keys für ein sehr praktisches Muster zu kombinieren:

javascript
m.route(document.body, '/edit/1', {
  '/edit/:key': Edit,
});

Dies bedeutet, dass der vnode, der für die Root-Komponente der Route erstellt wird, ein Routenparameter-Objekt key hat. Routenparameter werden zu attrs im vnode. Wenn man also von einer Seite zur anderen springt, ändert sich der Schlüssel, was dazu führt, dass die Komponente von Grund auf neu erstellt wird, da der Schlüssel der Virtual-DOM-Engine signalisiert, dass es sich um unterschiedliche Entitäten handelt.

Du kannst diese Idee noch weiterführen, um Komponenten zu erstellen, die sich beim Neuladen selbst neu erstellen:

m.route.set(m.route.get(), {key: Date.now()})

Oder verwende sogar das History State-Feature, um neu ladbare Komponenten zu erstellen, ohne die URL zu verunreinigen:

m.route.set(m.route.get(), null, {state: {key: Date.now()}})

Beachte, dass der Key-Parameter nur für Komponentenrouten funktioniert. Wenn du einen Route Resolver verwendest, musst du ein Single-Child Keyed Fragment verwenden und key: m.route.param("key") übergeben, um dasselbe zu erreichen.

Variadische Routen ​

Es ist auch möglich, variadische Routen zu haben, d. h. eine Route mit einem Argument, das URL-Pfadnamen enthält, die Schrägstriche enthalten:

javascript
m.route(document.body, '/edit/pictures/image.jpg', {
  '/edit/:file...': Edit,
});

Behandeln von 404s ​

Für isomorphe/universelle JavaScript-Anwendungen ist eine Kombination aus einem URL-Parameter und einer variadischen Route sehr nützlich, um eine benutzerdefinierte 404-Fehlerseite anzuzeigen.

Im Falle eines 404-Not-Found-Fehlers sendet der Server die benutzerdefinierte Seite an den Client zurück. Wenn Mithril.js geladen wird, leitet es den Client zur Standardroute um, da es nicht erkennen kann, dass diese Route existiert.

javascript
m.route(document.body, '/', {
  '/': homeComponent,
  // [...]
  '/:404...': errorPageComponent,
});

History State ​

Es ist möglich, die zugrunde liegende history.pushState-API voll auszunutzen, um die Navigationserfahrung des Benutzers zu verbessern. Beispielsweise könnte sich eine Anwendung den Zustand eines großen Formulars "merken", wenn der Benutzer eine Seite verlässt, indem er wegnavigiert, sodass der Benutzer beim Drücken der Zurück-Taste im Browser das Formular ausgefüllt hätte und nicht ein leeres Formular.

Beispielsweise könnte ein Formular wie folgt erstellt werden:

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

Auf diese Weise bleibt das Eingabefeld mit dem Suchbegriff gefüllt, wenn der Benutzer sucht und dann die Zurück-Taste drückt, um zur Anwendung zurückzukehren. Diese Technik kann die Benutzererfahrung von großen Formularen und anderen Apps verbessern, bei denen nicht persistierter Zustand für einen Benutzer mühsam zu erzeugen ist.

Ändern des Router-Präfixes ​

Das Router-Präfix ist ein Teil der URL, der die zugrunde liegende Strategie des Routers bestimmt.

javascript
// 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';

Fortgeschrittene Komponentenauflösung ​

Anstatt einer Route direkt eine Komponente zuzuweisen, können Sie ein RouteResolver-Objekt angeben. Ein RouteResolver-Objekt enthält eine onmatch()- und/oder eine render()-Methode. Beide Methoden sind optional, aber mindestens eine von ihnen muss vorhanden sein.

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function (args, requestedPath, route) {
      return Home;
    },
    render: function (vnode) {
      return vnode; // entspricht m(Home)
    },
  },
});

RouteResolver sind nützlich, um eine Vielzahl komplexer Routing-Anwendungsfälle zu implementieren.

Ein Layout um eine Komponente wickeln ​

Oft ist es wünschenswert, die meisten gerouteten Komponenten in eine wiederverwendbare Hülle (oft als "Layout" bezeichnet) zu packen. Dazu müssen Sie zuerst eine Komponente erstellen, die das gemeinsame Markup enthält, das die verschiedenen Komponenten umschließt:

javascript
var Layout = {
  view: function (vnode) {
    return m('.layout', vnode.children);
  },
};

Im obigen Beispiel besteht das Layout lediglich aus einem <div class="layout">, das die an die Komponente übergebenen Kindelemente enthält. In der Praxis kann es jedoch so komplex sein, wie es die Anwendung erfordert.

Eine Möglichkeit, das Layout zu integrieren, besteht darin, eine anonyme Komponente in der Routen-Konfiguration zu definieren:

javascript
// Beispiel 1
m.route(document.body, '/', {
  '/': {
    view: function () {
      return m(Layout, m(Home));
    },
  },
  '/form': {
    view: function () {
      return m(Layout, m(Form));
    },
  },
});

Beachten Sie jedoch, dass die oberste Komponente anonym ist. Beim Wechsel von der /-Route zur /form-Route (oder umgekehrt) wird die anonyme Komponente entfernt. Das DOM wird dann von Grund auf neu erstellt. Wenn die Layout-Komponente oninit- und oncreate-Lifecycle-Methoden hat, würden die oninit- und oncreate-Hooks bei jeder Routenänderung ausgelöst. Ob dies wünschenswert ist, hängt von der jeweiligen Anwendung ab.

Wenn Sie es vorziehen, dass die Layout-Komponente "gedifft" und intakt gehalten wird, anstatt sie von Grund auf neu zu erstellen, sollten Sie stattdessen einen RouteResolver als Root-Objekt verwenden:

javascript
// Beispiel 2
m.route(document.body, '/', {
  '/': {
    render: function () {
      return m(Layout, m(Home));
    },
  },
  '/form': {
    render: function () {
      return m(Layout, m(Form));
    },
  },
});

Beachten Sie, dass in diesem Fall die Layout-Komponente oninit- und oncreate-Lifecycle-Methoden hat, diese nur bei der ersten Routenänderung ausgelöst würden (vorausgesetzt, alle Routen verwenden dasselbe Layout).

Um den Unterschied zwischen den beiden Beispielen zu verdeutlichen, ist Beispiel 1 äquivalent zu diesem Code:

javascript
// funktional äquivalent zu Beispiel 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);
    },
  },
});

Da Anon1 und Anon2 unterschiedliche Komponenten sind, werden ihre Subtrees (einschließlich Layout) von Grund auf neu erstellt. Dies geschieht auch, wenn Komponenten direkt ohne RouteResolver verwendet werden.

In Beispiel 2, da Layout die Komponente der obersten Ebene in beiden Routen ist, wird das DOM für die Layout-Komponente verglichen (diffed) (d. h. intakt gelassen, wenn es keine Änderungen aufweist), und nur die Änderung von Home zu Form löst eine Neuerstellung dieses Abschnitts des DOM aus.

Weiterleitung ​

Mit dem onmatch-Hook des RouteResolver kann Logik ausgeführt werden, bevor die Komponente der obersten Ebene in einer Route initialisiert wird. Sie können entweder Mithrils m.route.set() oder die native HTML history-API verwenden. Bei der Weiterleitung mit der history-API muss der onmatch-Hook ein Promise zurückgeben, das niemals aufgelöst wird, um die Auflösung der übereinstimmenden Route zu verhindern. m.route.set() bricht die Auflösung der übereinstimmenden Route intern ab, sodass dies nicht erforderlich ist.

Beispiel: Authentifizierung ​

Das folgende Beispiel zeigt die Implementierung einer Login-Wall, die verhindert, dass Benutzer die /secret-Seite sehen, ohne sich anzumelden.

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

Wenn die Anwendung geladen wird, wird onmatch aufgerufen, und da isLoggedIn false ist, leitet die Anwendung zu /login weiter. Sobald der Benutzer die Login-Schaltfläche gedrückt hat, wird isLoggedIn auf true gesetzt, und die Anwendung leitet zu /secret weiter. Der onmatch-Hook würde erneut ausgeführt, und da isLoggedIn diesmal true ist, würde die Anwendung die Home-Komponente rendern.

Im obigen Beispiel wird der Anmeldestatus des Benutzers der Einfachheit halber in einer globalen Variable gespeichert. Dieses Flag wird lediglich umgeschaltet, wenn der Benutzer auf die Login-Schaltfläche klickt. In einer realen Anwendung müsste ein Benutzer natürlich die richtigen Anmeldeinformationen angeben, und das Klicken auf die Login-Schaltfläche würde eine Anfrage an einen Server auslösen, um den Benutzer zu authentifizieren:

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

Vorabladen von Daten ​

Normalerweise kann eine Komponente Daten bei der Initialisierung laden. Dies führt dazu, dass die Komponente zweimal gerendert wird. Der erste Render-Pass erfolgt beim Routing, und der zweite wird nach Abschluss der Anfrage ausgelöst. Beachten Sie, dass loadUsers() ein Promise zurückgibt, aber jedes von oninit zurückgegebene Promise wird derzeit ignoriert. Der zweite Render-Pass stammt von der background-Option für m.request.

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

Im obigen Beispiel zeigt die UI beim ersten Rendern "loading" an, da state.users vor Abschluss der Anfrage ein leeres Array ist. Sobald die Daten verfügbar sind, wird die UI neu gezeichnet und eine Liste von Benutzer-IDs angezeigt.

RouteResolver können verwendet werden, um Daten vor dem Rendern einer Komponente vorab zu laden. Dadurch wird Bildschirmflackern vermieden und die Notwendigkeit einer Ladeanzeige umgangen.

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

Oben wird render erst nach Abschluss der Anfrage ausgeführt, wodurch der ternäre Operator überflüssig wird.

Code-Splitting ​

In großen Anwendungen kann es wünschenswert sein, den Code für jede Route bei Bedarf herunterzuladen, anstatt ihn im Voraus zu laden. Diese Art der Codeaufteilung wird als Code-Splitting oder Lazy Loading bezeichnet. In Mithril.js kann dies erreicht werden, indem ein Promise vom onmatch-Hook zurückgegeben wird:

In seiner einfachsten Form könnte man Folgendes tun:

javascript
// Home.js
module.export = {
  view: function () {
    return [m(Menu), m('h1', 'Home')];
  },
};
javascript
// 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');
    },
  },
});

Damit dies jedoch in einem Produktionsmaßstab funktioniert, ist es realistischerweise erforderlich, alle Abhängigkeiten für das Home.js-Modul in die Datei zu bündeln, die letztendlich vom Server bereitgestellt wird.

Glücklicherweise gibt es eine Reihe von Tools, die die Aufgabe des Bündelns von Modulen für Lazy Loading erleichtern. Hier ist ein Beispiel mit native dynamic import(...), das von vielen Bundlern unterstützt wird:

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function () {
      return import('./Home.js');
    },
  },
});

Typisierte Routen ​

In bestimmten komplexen Routing-Fällen kann es sinnvoll sein, einen Wert stärker einzuschränken als nur den Pfad selbst, beispielsweise auf eine numerische ID. Sie können dies ganz einfach tun, indem Sie m.route.SKIP von einer Route zurückgeben.

javascript
m.route(document.body, '/', {
  '/view/:id': {
    onmatch: function (args) {
      if (!/^\d+$/.test(args.id)) return m.route.SKIP;
      return ItemView;
    },
  },
  '/view/:name': UserView,
});

Versteckte Routen ​

Es kann vorkommen, dass ein Benutzer einen bestimmten Benutzer nicht anzeigen darf. Anstatt einen Zugriffsfehler anzuzeigen, kann es in diesem Fall sinnvoll sein, so zu tun, als ob der Benutzer nicht existiert, und stattdessen zu einer 404-Seite weiterzuleiten. In diesem Fall können Sie m.route.SKIP verwenden, um einfach so zu tun, als ob die Route nicht existiert.

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

Routenabbruch / Blockierung ​

Der onmatch-Hook des RouteResolver kann die Routenauflösung verhindern, indem ein Promise zurückgegeben wird, das niemals aufgelöst wird. Dies kann verwendet werden, um versuchte redundante Routenauflösungen zu erkennen und abzubrechen:

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function (args, requestedPath) {
      if (m.route.get() === requestedPath) return new Promise(function () {});
    },
  },
});

Integration von Drittanbietern ​

In bestimmten Situationen kann es erforderlich sein, mit einem anderen Framework wie React zusammenzuarbeiten. So geht's:

  • Definieren Sie alle Ihre Routen wie gewohnt mit m.route, achten Sie jedoch darauf, es nur einmal zu verwenden. Mehrere Routenpunkte werden nicht unterstützt.
  • Wenn Sie Routing-Beobachter entfernen müssen, verwenden Sie m.mount(root, null) und verwenden Sie denselben Root, den Sie für m.route(root, ...) verwendet haben. m.route verwendet intern m.mount, um alles zu verbinden.

Hier ist ein Beispiel mit React:

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

Und hier ist das grobe Äquivalent mit Vue:

html
<div ref="root"></div>
javascript
Vue.component('my-child', {
  template: `<div ref="root"></div>`,
  mounted: function () {
    m.route(this.$refs.root, '/', {
      // ...
    });
  },
  destroyed: function () {
    m.mount(this.$refs.root, null);
  },
});
Pager
Vorherige Seitemount(root, component)
Nächste Seiterequest(options)

Veröffentlicht unter der MIT-Lizenz.

Copyright (c) 2024 Mithril Contributors

https://mithril.js.org/route.html

Veröffentlicht unter der MIT-Lizenz.

Copyright (c) 2024 Mithril Contributors