Skip to content
Mithril.js 2
Main Navigation PrzewodnikAPI

Polski

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

Polski

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

Wygląd

Sidebar Navigation

API

Podstawowe 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(obiekt, extra)

Opcjonalne API

stream()

Przewodnik

Na tej stronie

route(root, defaultRoute, routes) ​

Opis ​

Umożliwia nawigację między "stronami" w aplikacji.

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

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

W aplikacji może istnieć tylko jedno wywołanie m.route.

Sygnatura ​

m.route(root, defaultRoute, routes)

ArgumentTypWymaganyOpis
rootElementTakElement DOM, który będzie elementem nadrzędnym dla renderowanego poddrzewa.
defaultRouteStringTakTrasa, do której nastąpi przekierowanie, jeśli bieżący adres URL nie pasuje do żadnej zdefiniowanej trasy. Uwaga: Nie jest to trasa startowa aplikacji. Jako trasa startowa zostanie użyty adres URL z paska adresu przeglądarki.
routesObject<String,Component|RouteResolver>TakObiekt, którego klucze są łańcuchami znaków reprezentującymi trasy, a wartościami są komponenty lub RouteResolver.
zwracaBrak zwracanej wartości.

Jak czytać sygnatury

Statyczne elementy członkowskie ​

m.route.set ​

Przekierowuje do pasującej trasy lub do trasy domyślnej, jeśli nie można znaleźć pasującej trasy. Wyzwala asynchroniczne ponowne renderowanie wszystkich zamontowanych komponentów.

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

ArgumentTypWymaganyOpis
pathStringTakNazwa ścieżki, do której ma nastąpić przekierowanie, bez prefiksu. Ścieżka może zawierać parametry, które zostaną interpolowane z wartościami z params.
paramsObjectNieParametry routingu. Jeśli path zawiera symbole zastępcze dla parametrów routingu, właściwości tego obiektu zostaną użyte do interpolacji łańcucha ścieżki.
options.replaceBooleanNieOkreśla, czy utworzyć nowy wpis w historii przeglądarki, czy zastąpić bieżący. Domyślnie: false.
options.stateObjectNieObiekt state przekazywany do bazowej metody history.pushState / history.replaceState. Ten obiekt staje się dostępny poprzez właściwość history.state i jest scalany z parametrami routingu. Opcja ta działa tylko w przypadku korzystania z API pushState. Jest ignorowana, gdy router przechodzi w tryb hashchange (tzn. gdy API pushState jest niedostępne).
options.titleStringNieTekst, który zostanie przekazany jako tytuł do bazowej metody history.pushState / history.replaceState.
zwracaZwraca undefined.

Pamiętaj, że używając .set z params, musisz również zdefiniować odpowiednią trasę:

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 ​

Zwraca ostatnią w pełni rozwiązaną ścieżkę routingu, bez prefiksu. Może się różnić od ścieżki widocznej w pasku adresu przeglądarki, gdy asynchroniczne ładowanie trasy jest w toku (code-splitting).

path = m.route.get()

ArgumentTypWymaganyOpis
zwracaStringZwraca ostatnią w pełni rozwiązaną ścieżkę.

m.route.prefix ​

Definiuje prefiks routera. Prefiks routera to fragment adresu URL, który określa bazową strategię używaną przez router.

m.route.prefix = prefix

ArgumentTypWymaganyOpis
prefixStringTakPrefiks, który kontroluje bazową strategię routingu używaną przez Mithril.

Jest to właściwość, którą można zarówno odczytywać, jak i zapisywać.

m.route.Link ​

Ten komponent tworzy dynamiczne linki nawigacyjne. Jego głównym zadaniem jest generowanie elementów <a>, których atrybut href jest modyfikowany w oparciu o prefiks trasy.

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

// O ile m.route.prefix nie zmienił się od domyślnej strategii, renderuje do:
// <a href="#!/foo">foo</a>

Linki akceptują następujące specjalne atrybuty:

  • selector to to, co zostałoby przekazane jako pierwszy argument do m: dowolny selektor jest ważny, w tym elementy inne niż a.
  • params & options to argumenty o tych samych nazwach, co zdefiniowane w m.route.set.
  • disabled, jeśli true, wyłącza zachowanie routingu i dowolny powiązany handler onclick oraz dołącza atrybut data-disabled="true" dla wskazówek dotyczących dostępności; jeśli element jest a, href jest usuwany.

Domyślne zachowanie routingu nie może zostać anulowane za pomocą standardowych metod obsługi zdarzeń. Zamiast tego należy użyć atrybutu disabled.

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

// Renderuje do:
// <button disabled aria-disabled="true" class="large">link name</button>

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

ArgumentTypWymaganyOpis
attributes.hrefObjectTakDocelowa trasa do nawigacji.
attributes.disabledBooleanNieWyłącza element w sposób dostępny.
attributes.selectorString|Object|FunctionNieSelektor dla m, domyślnie "a".
attributes.optionsObjectNieUstawia options przekazywane do m.route.set.
attributes.paramsObjectNieUstawia params przekazywane do m.route.set.
attributesObjectNieWszelkie inne atrybuty, które mają być przekazane do m.
childrenArray<Vnode>|String|Number|BooleanNieDziecięce vnode dla tego linku.
zwracaVnodeVnode.

m.route.param ​

Pobiera parametr trasy z ostatniej w pełni rozwiązanej trasy. Parametry trasy to pary klucz-wartość. Parametry trasy mogą pochodzić z kilku różnych źródeł:

  • Interpolacje tras (np. jeśli trasa to /users/:id, a rozwiązuje się do /users/1, parametr trasy ma klucz id i wartość "1").
  • Łańcuchy zapytań routera (np. jeśli ścieżka to /users?page=1, parametr trasy ma klucz page i wartość "1").
  • history.state (np. jeśli history.state to {foo: "bar"}, parametr trasy ma klucz foo i wartość "bar").

value = m.route.param(key)

ArgumentTypWymaganyOpis
keyStringNieNazwa parametru trasy (np. id w trasie /users/:id lub page w ścieżce /users/1?page=3 lub klucz w history.state).
zwracaString|ObjectZwraca wartość dla określonego klucza. Jeśli klucz nie jest określony, zwraca obiekt, który zawiera wszystkie klucze interpolacji.

Zauważ, że w funkcji onmatch RouteResolvera nowa trasa nie została jeszcze w pełni rozwiązana, a m.route.param() zwróci parametry poprzedniej trasy, jeśli takie istnieją. onmatch otrzymuje parametry nowej trasy jako argument.

m.route.SKIP ​

Specjalna wartość, która może być zwrócona z funkcji onmatch resolvera trasy, aby przejść do następnej trasy.

RouteResolver ​

RouteResolver to obiekt, który nie jest komponentem i zawiera metodę onmatch i/lub metodę render. Obie metody są opcjonalne, ale przynajmniej jedna musi być obecna.

Jeśli dany obiekt posiada metodę view lub jest funkcją/klasą (co wskazuje na to, że jest komponentem), zostanie potraktowany jako komponent, nawet jeśli zdefiniowano w nim metody onmatch lub render. RouteResolver nie jest komponentem, dlatego nie posiada metod cyklu życia.

Z reguły RouteResolvers powinny znajdować się w tym samym pliku co wywołanie m.route, podczas gdy definicje komponentów powinny znajdować się w ich własnych modułach.

routeResolver = {onmatch, render}

Można traktować komponenty jako uproszczoną wersję resolvera trasy. Przykładowo, jeśli komponent nazywa się Home…

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

routeResolver.onmatch ​

Metoda onmatch jest wywoływana przez router w celu znalezienia komponentu, który ma zostać wyrenderowany. Wywoływana jest raz przy każdej zmianie ścieżki, ale nie podczas kolejnych aktualizacji (ponownych renderowań) tej samej ścieżki. Może być używana do wykonania logiki przed zainicjowaniem komponentu (np. uwierzytelnianie, wstępne ładowanie danych, śledzenie analityczne przekierowań).

Ta metoda pozwala również asynchronicznie zdefiniować, który komponent zostanie wyrenderowany, dzięki czemu nadaje się do dzielenia kodu i asynchronicznego ładowania modułów. Aby wyrenderować komponent asynchronicznie, zwróć obietnicę, która rozwiązuje się do komponentu.

Więcej informacji na temat onmatch można znaleźć w sekcji zaawansowane rozwiązywanie komponentów.

routeResolver.onmatch(args, requestedPath, route)

ArgumentTypOpis
argsObjectParametry routingu.
requestedPathStringŚcieżka routera żądana przez ostatnią akcję routingu, w tym interpolowane wartości parametrów routingu, ale bez prefiksu. Gdy onmatch jest wywoływany, rozwiązywanie dla tej ścieżki nie jest zakończone, a m.route.get() nadal zwraca poprzednią ścieżkę.
routeStringŚcieżka routera żądana przez ostatnią akcję routingu, z wyłączeniem interpolowanych wartości parametrów routingu.
zwracaComponent|\Promise<Component>|undefinedZwraca komponent lub obietnicę, która rozwiązuje się do komponentu.

Jeśli onmatch zwraca komponent lub obietnicę, która rozwiązuje się do komponentu, ten komponent jest używany jako vnode.tag dla pierwszego argumentu w metodzie render RouteResolvera. W przeciwnym razie vnode.tag jest ustawiony na "div". Podobnie, jeśli metoda onmatch zostanie pominięta, vnode.tag również wynosi "div".

Jeśli onmatch zwraca obietnicę, która zostaje odrzucona, router przekierowuje z powrotem do defaultRoute. Możesz zastąpić to zachowanie, wywołując .catch na łańcuchu obietnic przed jego zwróceniem.

routeResolver.render ​

Metoda render jest wywoływana przy każdym ponownym renderowaniu dla pasującej trasy. Jest podobna do metody view w komponentach i istnieje, aby uprościć kompozycję komponentów. Pozwala to również na uniknięcie domyślnego zachowania Mithril.js, które polega na zastępowaniu całego poddrzewa DOM.

vnode = routeResolver.render(vnode)

ArgumentTypOpis
vnodeObjectVnode, którego obiekt atrybutów zawiera parametry routingu. Jeśli onmatch nie zwraca komponentu lub obietnicy, która rozwiązuje się do komponentu, pole tag vnode domyślnie przyjmuje wartość "div".
vnode.attrsObjectMapa wartości parametrów URL.
zwracaArray<Vnode>|VnodeVnode do wyrenderowania.

Parametr vnode to po prostu m(Component, m.route.param()), gdzie Component to rozwiązany komponent dla trasy (po routeResolver.onmatch), a m.route.param() jest udokumentowany tutaj. Jeśli pominiesz tę metodę, domyślna wartość zwracana to [vnode], opakowana we fragment, dzięki czemu możesz użyć parametrów klucza. W połączeniu z parametrem :key staje się fragmentem z kluczem pojedynczego elementu, ponieważ kończy się renderowaniem do czegoś takiego jak [m(Component, {key: m.route.param("key"), ...})].

Jak to działa ​

Routing to system, który pozwala na tworzenie aplikacji jednostronicowych (SPA), tj. aplikacji, które mogą przechodzić z jednej "strony" do drugiej bez powodowania pełnego odświeżenia przeglądarki.

Umożliwia płynną nawigację, zachowując jednocześnie możliwość dodawania zakładek do każdej strony indywidualnie oraz możliwość nawigacji po aplikacji za pomocą mechanizmu historii przeglądarki.

Routing bez odświeżania strony jest częściowo możliwy dzięki API history.pushState. Korzystając z tego API, można programowo zmienić adres URL wyświetlany przez przeglądarkę po załadowaniu strony, ale to na deweloperze aplikacji spoczywa odpowiedzialność za zapewnienie, że nawigacja do dowolnego adresu URL ze stanu zimnego (np. nowa karta) spowoduje wyrenderowanie odpowiedniego znacznika.

Strategie routingu ​

Strategia routingu określa, w jaki sposób biblioteka implementuje routing. Istnieją trzy ogólne strategie, które można wykorzystać do implementacji systemu routingu SPA, a każda z nich ma inne ograniczenia:

  • m.route.prefix = '#!' (domyślna) – Używanie identyfikatora fragmentu (znanego również jako hash) części adresu URL. Adres URL korzystający z tej strategii zazwyczaj wygląda tak: https://localhost/#!/page1.
  • m.route.prefix = '?' – Używanie łańcucha zapytania. Adres URL korzystający z tej strategii zazwyczaj wygląda tak: https://localhost/?/page1.
  • m.route.prefix = '' – Używanie nazwy ścieżki. Adres URL korzystający z tej strategii zazwyczaj wygląda tak: https://localhost/page1.

Użycie strategii hash jest gwarantowane w przeglądarkach, które nie obsługują history.pushState, ponieważ może ona powrócić do używania onhashchange. Użyj tej strategii, jeśli chcesz zachować hashe wyłącznie lokalnie.

Strategia łańcucha zapytania umożliwia wykrywanie po stronie serwera, ale nie pojawia się jako normalna ścieżka. Użyj tej strategii, jeśli chcesz obsługiwać i potencjalnie wykrywać zakotwiczone linki po stronie serwera i nie możesz wprowadzić zmian niezbędnych do obsługi strategii nazwy ścieżki (tak jakbyś używał Apache i nie możesz modyfikować swojego .htaccess).

Strategia nazwy ścieżki tworzy najczystsze adresy URL, ale wymaga skonfigurowania serwera do obsługi kodu aplikacji jednostronicowej z każdego adresu URL, do którego aplikacja może kierować. Użyj tej strategii, jeśli chcesz uzyskać czystsze adresy URL.

Aplikacje jednostronicowe, które używają strategii hash, często używają konwencji umieszczania wykrzyknika po hash, aby wskazać, że używają hash jako mechanizmu routingu, a nie do celów łączenia z kotwicami. Łańcuch #! jest znany jako hashbang.

Domyślna strategia używa hashbang.

Typowe użycie ​

Zwykle musisz utworzyć kilka komponentów, aby mapować trasy do:

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

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

W powyższym przykładzie istnieją dwa komponenty: Home i Page1. Każdy zawiera menu i trochę tekstu. Menu samo w sobie jest definiowane jako komponent, aby uniknąć powtórzeń:

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

Teraz możemy zdefiniować trasy i mapować nasze komponenty do nich:

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

Tutaj określamy dwie trasy: / i /page1, które renderują swoje odpowiednie komponenty, gdy użytkownik przechodzi do każdego adresu URL.

Nawigacja do różnych tras ​

W powyższym przykładzie komponent Menu zawiera dwa elementy m.route.Link. Powoduje to utworzenie elementu (domyślnie <a>), który po kliknięciu przez użytkownika, przenosi go do innej trasy w aplikacji. Nawigacja odbywa się lokalnie, bez odwoływania się do serwera.

Możesz również nawigować programowo, za pomocą m.route.set(route). Na przykład m.route.set("/page1").

Podczas nawigacji między trasami, prefiks routera jest obsługiwany automatycznie. Innymi słowy, pomiń hashbang #! (lub cokolwiek ustawisz m.route.prefix na) podczas łączenia tras Mithril.js, w tym zarówno w m.route.set, jak i w m.route.Link.

Zauważ, że podczas nawigacji między komponentami, całe poddrzewo jest zastępowane. Jeśli chcesz jedynie zaktualizować fragment drzewa DOM, użyj resolver trasy z zaimplementowaną metodą render.

Parametry routingu ​

Czasami chcemy, aby zmienne identyfikatory lub podobne dane pojawiały się w trasie, ale nie chcemy jawnie określać oddzielnej trasy dla każdego możliwego identyfikatora. Aby to osiągnąć, Mithril.js obsługuje parametryzowane trasy:

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

W powyższym przykładzie zdefiniowaliśmy trasę /edit/:id. To tworzy dynamiczną trasę, która pasuje do dowolnego adresu URL, który zaczyna się od /edit/ i jest kontynuowany przez jakieś dane (np. /edit/1, edit/234 itp.). Wartość id jest następnie mapowana jako atrybut vnode komponentu (vnode.attrs.id).

Możliwe jest posiadanie wielu argumentów w trasie, na przykład /edit/:projectID/:userID dałoby właściwości projectID i userID w obiekcie atrybutów vnode komponentu.

Parametr klucza ​

Gdy użytkownik przechodzi z trasy sparametryzowanej do tej samej trasy z innym parametrem (np. przechodząc z /page/1 do /page/2 przy danej trasie /page/:id), komponent nie zostałby odtworzony od zera, ponieważ obie trasy rozwiązują się do tego samego komponentu, a tym samym powodują różnicę wirtualnego dom w miejscu. Skutkuje to wywołaniem metody onupdate zamiast oninit / oncreate.

Aby to osiągnąć, można połączyć parametryzację trasy z kluczami dla bardzo wygodnego wzorca:

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

Oznacza to, że vnode, który jest tworzony dla głównego komponentu trasy, ma obiekt parametru trasy key. Parametry trasy stają się attrs w vnode. Tak więc, przeskakując z jednej strony na drugą, key zmienia się i powoduje odtworzenie komponentu od zera (ponieważ klucz mówi silnikowi wirtualnego dom, że stare i nowe komponenty są różnymi bytami).

Można to wykorzystać do tworzenia komponentów, które odświeżają się przy każdej zmianie trasy:

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

Lub nawet użyć funkcji history state, aby osiągnąć komponenty z możliwością ponownego załadowania bez zanieczyszczania adresu URL:

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

Zauważ, że parametr klucza działa tylko dla tras komponentów. Jeśli używasz resolvera trasy, musisz użyć fragmentu z kluczem pojedynczego elementu, przekazując key: m.route.param("key"), aby osiągnąć to samo.

Trasy wariadyczne ​

Możliwe jest również posiadanie tras wariadycznych, tj. trasy z argumentem, który zawiera nazwy ścieżek URL, które zawierają ukośniki:

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

Obsługa błędów 404 ​

W przypadku aplikacji izomorficznych (uniwersalnych) pisanych w JavaScript, przekazywanie parametru URL w połączeniu z trasą wariadyczną jest przydatne do wyświetlania niestandardowej strony błędu 404.

W przypadku błędu 404 Not Found, serwer odsyła niestandardową stronę do klienta. Gdy Mithril.js jest załadowany, przekieruje klienta do domyślnej trasy, ponieważ nie może wiedzieć, że trasa.

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

Stan historii ​

Możliwe jest pełne wykorzystanie bazowego API history.pushState, aby poprawić komfort nawigacji użytkownika. Na przykład, aplikacja mogłaby "zapamiętać" stan dużego formularza, gdy użytkownik opuszcza stronę, oddalając się, tak że jeśli użytkownik naciśnie przycisk wstecz w przeglądarce, miałby wypełniony formularz, a nie pusty formularz.

Na przykład, możesz utworzyć formularz w ten sposób:

javascript
var state = {
  term: '',
  search: function () {
    m.route.set(m.route.get(), null, {
      replace: true,
      state: { term: state.term },
    });

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

Dzięki temu, jeśli użytkownik wprowadzi dane w formularzu, a następnie cofnie się do niego za pomocą przycisku „Wstecz” w przeglądarce, pole wejściowe nadal będzie zawierało wprowadzone wcześniej dane. Technika ta poprawia komfort użytkowania, szczególnie w przypadku rozbudowanych formularzy, gdzie ponowne wprowadzanie danych byłoby uciążliwe.

Zmiana prefiksu routera ​

Prefiks routera to fragment adresu URL, który określa bazową strategię używaną przez router.

javascript
m.route.prefix = ''; // strategia nazwy ścieżki dla URL niebędącego głównym

m.route.prefix = '?';

m.route.prefix = '#';

// strategia nazwy ścieżki dla URL niebędącego głównym
m.route.prefix = '/my-app';

Zaawansowane zarządzanie komponentami ​

Zamiast przypisywać komponent bezpośrednio do trasy, możesz zdefiniować obiekt RouteResolver. Obiekt RouteResolver zawiera metodę onmatch() i/lub render(). Obie metody są opcjonalne, ale przynajmniej jedna z nich musi być zdefiniowana.

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

RouteResolvers są przydatne do implementacji zaawansowanych scenariuszy routingu.

Opakowywanie komponentu układu ​

Często istnieje potrzeba opakowania wszystkich lub większości routowanych komponentów w kontener wielokrotnego użytku (często nazywany układem). Aby to osiągnąć, najpierw stwórz komponent, który zawiera wspólny znacznik, który będzie opakowywał inne komponenty:

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

W powyższym przykładzie układ składa się jedynie z <div class="layout">, który zawiera elementy potomne przekazane do komponentu, ale w rzeczywistym scenariuszu może być on bardziej złożony.

Jednym ze sposobów opakowania układu jest zdefiniowanie anonimowego komponentu w mapie tras:

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

Pamiętaj jednak, że komponent główny jest komponentem anonimowym. Przejście z trasy / do trasy /form (lub odwrotnie) spowoduje usunięcie anonimowego komponentu i ponowne utworzenie DOM od zera. Jeśli komponent Layout miał zdefiniowane metody cyklu życia, haki oninit i oncreate uruchamiałyby się przy każdej zmianie trasy. W zależności od aplikacji, może to być pożądane lub nie.

Jeśli wolisz, aby komponent Layout był różnicowany i utrzymywany w stanie nienaruszonym, zamiast odtwarzania go od zera, powinieneś użyć RouteResolver jako obiektu głównego:

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

Zauważ, że w tym przypadku, jeśli komponent Layout ma metody cyklu życia oninit i oncreate, zostaną one uruchomione tylko przy pierwszej zmianie trasy (zakładając, że wszystkie trasy używają tego samego układu).

Aby wyjaśnić różnicę między dwoma przykładami, przykład 1 jest równoważny z tym kodem:

javascript
// funkcjonalnie równoważne z przykładem 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);
    },
  },
});

Ponieważ Anon1 i Anon2 są różnymi komponentami, ich poddrzewa (w tym Layout) są odtwarzane od zera. Dzieje się tak również, gdy komponenty są używane bezpośrednio bez RouteResolver.

W przykładzie 2, ponieważ Layout jest komponentem najwyższego poziomu w obu trasach, DOM dla komponentu Layout jest aktualizowany (tj. pozostawiany bez zmian, jeśli nie ma różnic), a jedynie zmiana z Home na Form powoduje odtworzenie tej sekcji DOM.

Przekierowanie ​

Funkcja onmatch w RouteResolver może być używana do uruchamiania logiki przed zainicjowaniem komponentu najwyższego poziomu dla danej trasy. Możesz użyć albo m.route.set() Mithrila, albo natywnego API history HTML. Podczas przekierowywania za pomocą API history, hak onmatch musi zwrócić obietnicę, która nigdy nie zostanie rozwiązana, aby zapobiec rozwiązaniu dopasowanej trasy. m.route.set() anuluje rozwiązywanie dopasowanej trasy wewnętrznie, więc nie jest to konieczne w jego przypadku.

Przykład: uwierzytelnianie ​

Poniższy przykład ilustruje, jak zaimplementować ekran logowania, który uniemożliwia użytkownikom oglądanie strony /secret, chyba że się zalogują.

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

Po załadowaniu aplikacji wywoływana jest funkcja onmatch i ponieważ isLoggedIn ma wartość false, aplikacja przekierowuje do /login. Po naciśnięciu przycisku logowania, wartość isLoggedIn zostanie ustawiona na true, a aplikacja przekieruje do /secret. Hak onmatch zostanie uruchomiony ponownie i ponieważ isLoggedIn ma tym razem wartość true, aplikacja wyrenderuje komponent Home.

Dla uproszczenia, w powyższym przykładzie, status zalogowania użytkownika przechowywany jest w zmiennej globalnej, a flaga ta jest jedynie przełączana, gdy użytkownik kliknie przycisk logowania. W rzeczywistej aplikacji użytkownik oczywiście musiałby podać odpowiednie dane logowania, a kliknięcie przycisku logowania spowodowałoby wysłanie żądania do serwera w celu uwierzytelnienia użytkownika:

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

Wstępne ładowanie danych ​

Zazwyczaj komponent może ładować dane po jego inicjalizacji. Ładowanie danych w ten sposób powoduje dwukrotne renderowanie komponentu. Pierwsze przejście renderowania następuje po routingu, a drugie po zakończeniu żądania. Należy pamiętać, że loadUsers() zwraca obietnicę, ale każda obietnica zwrócona przez oninit jest obecnie ignorowana. Drugie przejście renderowania pochodzi z opcji background dla 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';
    },
  },
});

W powyższym przykładzie, podczas pierwszego renderowania, interfejs użytkownika wyświetla "loading" (ładowanie), ponieważ state.users jest pustą tablicą przed zakończeniem żądania. Następnie, gdy dane są dostępne, interfejs użytkownika jest ponownie renderowany i wyświetlana jest lista identyfikatorów użytkowników.

RouteResolvers mogą być używane jako mechanizm wstępnego ładowania danych przed renderowaniem komponentu, co pozwala uniknąć migotania interfejsu użytkownika i eliminuje potrzebę wyświetlania wskaźnika ładowania:

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

Powyżej, render uruchamia się dopiero po zakończeniu żądania, co czyni operator trójargumentowy zbędnym.

Dzielenie kodu ​

W dużej aplikacji może być pożądane, aby pobierać kod dla każdej trasy na żądanie, zamiast z góry. Dzielenie bazy kodu w ten sposób jest znane jako dzielenie kodu (code splitting) lub ładowanie opóźnione (lazy loading). W Mithril.js można to osiągnąć, zwracając obietnicę z haka onmatch:

W najprostszej formie można to zrobić następująco:

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

Jednak w rzeczywistości, aby to działało na skalę produkcyjną, konieczne byłoby spakowanie wszystkich zależności dla modułu Home.js do pliku, który jest ostatecznie obsługiwany przez serwer.

Na szczęście istnieje wiele narzędzi, które ułatwiają zadanie pakowania modułów do leniwego ładowania. Oto przykład użycia natywnego dynamicznego import(...), obsługiwanego przez wiele bundlerów:

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

Typowane trasy ​

W niektórych zaawansowanych przypadkach routingu możesz chcieć bardziej ograniczyć wartość niż tylko samą ścieżkę, dopasowując tylko coś takiego jak numeryczny identyfikator. Możesz to zrobić dość łatwo, zwracając m.route.SKIP z trasy.

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

Ukryte trasy ​

W rzadkich przypadkach możesz chcieć ukryć niektóre trasy przed niektórymi użytkownikami, ale nie dla wszystkich. Na przykład, użytkownik może nie mieć uprawnień do wyświetlania profilu innego użytkownika i zamiast wyświetlać błąd uprawnień, chcemy ukryć fakt jego istnienia i przekierować do widoku 404. W takim przypadku możesz użyć m.route.SKIP, aby po prostu udawać, że trasa nie istnieje.

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

Anulowanie lub blokowanie trasy ​

RouteResolver onmatch może zapobiec rozwiązaniu trasy, zwracając obietnicę, która nigdy się nie rozwiązuje. Można to wykorzystać do wykrywania prób ponownego, niepotrzebnego rozwiązywania tej samej trasy i ich anulowania:

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

Integracja z zewnętrznymi bibliotekami ​

W niektórych sytuacjach możesz potrzebować współpracy z innym frameworkiem, takim jak React. Oto jak to zrobić:

  • Zdefiniuj wszystkie swoje trasy za pomocą m.route jak zwykle, ale upewnij się, że używasz go tylko raz. Wiele punktów routingu nie jest obsługiwanych.
  • Aby usunąć subskrypcje routingu, użyj m.mount(root, null), podając ten sam element root, który został użyty w m.route(root, ...) . m.route używa wewnętrznie m.mount do podłączenia wszystkiego, więc to nie magia.

Oto przykład z 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} />;
  }
}

A oto z grubsza odpowiednik z 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
Poprzednia stronamount(root, component)
Następna stronarequest(options)

Opublikowano na licencji MIT.

Copyright (c) 2024 Mithril Contributors

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

Opublikowano na licencji MIT.

Copyright (c) 2024 Mithril Contributors