Skip to content
Mithril.js 2
Main Navigation ガイドAPI

日本語

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

日本語

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

外観

Sidebar Navigation

API

コア API

m(selector, attributes, children)

render(element, vnodes)

mount(root, component)

route(root, defaultRoute, routes)

request(options)

parseQueryString(string)

buildQueryString(object)

buildPathname(object)

parsePathname(文字列型)

trust(html)

fragment(attrs, children)

redraw()

censor(object, extra)

オプション API

stream()

ガイド

このページの内容

route(root, defaultRoute, routes) ​

Description ​

アプリケーション内の「ページ」間のナビゲーションを制御します。

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

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

1つのアプリケーション内で m.route は一度だけ呼び出すことができます。

Signature ​

m.route(root, defaultRoute, routes)

引数型必須説明
rootElementはい子要素の親ノードとなる DOM 要素
defaultRouteStringはい現在の URL がどのルートにも一致しない場合にリダイレクトするルート。これは初期ルートではありません。初期ルートはアドレスバーの URL によって決定されます。
routesObject<String,Component|RouteResolver>はいキーがルート文字列、値がコンポーネントまたは RouteResolver であるオブジェクト
戻り値undefined を返します

シグネチャの読み方

静的メンバー ​

m.route.set ​

指定されたルートにリダイレクトします。一致するルートが見つからない場合は、デフォルトルートにリダイレクトします。すべてのマウントポイントで非同期に再描画をトリガーします。

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

引数型必須説明
pathStringはいパス名(プレフィックスなし)。パスには、params の値で補間されるパラメータを含めることができます。
paramsObjectいいえルーティングパラメータ。path にルーティングパラメータのスロットがある場合、このオブジェクトのプロパティがパス文字列に埋め込まれます。
options.replaceBooleanいいえ新しい履歴エントリを作成するか、現在のエントリを置き換えるかを指定します。デフォルトは false です。
options.stateObjectいいえ基になる history.pushState / history.replaceState 呼び出しに渡す state オブジェクト。この state オブジェクトは history.state プロパティで使用可能になり、ルーティングパラメータオブジェクトにマージされます。このオプションは pushState API を使用する場合にのみ有効で、ルーターが hashchange モードにフォールバックする場合は無視されることに注意してください(つまり、pushState API が利用できない場合)。
options.titleStringいいえ基になる history.pushState / history.replaceState 呼び出しに渡す title 文字列。
戻り値undefined を返します

.set を params と一緒に使用する場合は、対応するルートを定義する必要があることに注意してください。

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 ​

プレフィックスなしで、最後に完全に解決されたルーティングパスを返します。非同期ルートが解決を待機している間、ロケーションバーに表示されるパスとは異なる場合があります。

path = m.route.get()

引数型必須説明
戻り値String最後に完全に解決されたパスを返します

m.route.prefix ​

ルーターのプレフィックスを定義します。ルーターのプレフィックスは、ルーターが使用する基盤となる戦略を決定する URL の一部です。

m.route.prefix = prefix

引数型必須説明
prefixStringはいMithril が使用する基盤となるルーティング戦略を制御するプレフィックス。

これは単純なプロパティなので、読み書き可能です。

m.route.Link ​

このコンポーネントは、動的なルーティングリンクを作成します。主な機能は、ローカルな href を持つ a リンクを生成し、ルートプレフィックスを考慮して変換することです。

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

// m.route.prefix がデフォルトから変更されていない限り、以下のようにレンダリングされます。
// <a href="#!/foo">foo</a>

リンクは以下の特別な属性を受け入れます。

  • selector は、m の最初の引数として渡されるものです。a 要素以外を含む、任意のセレクターが有効です。
  • params と options は、m.route.set で定義されているものと同じ引数です。
  • disabled が true の場合、ルーティングの動作とバインドされた onclick ハンドラが無効になり、アクセシビリティのヒントとして data-disabled="true" 属性が付与されます。要素が a の場合、href は削除されます。

ルーティング処理は、イベント処理 API を使用して阻止することはできません。代わりに disabled を使用してください。

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

// 以下のようにレンダリングされます。
// <button disabled aria-disabled="true" class="large">link name</button>

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

引数型必須説明
attributes.hrefObjectはいナビゲート先のターゲットルート。
attributes.disabledBooleanいいえ要素へのアクセスを無効にします。
attributes.selectorString|Object|Functionいいえm のセレクター。デフォルトは "a"。
attributes.optionsObjectいいえm.route.set に渡される options を設定します。
attributes.paramsObjectいいえm.route.set に渡される params を設定します。
attributesObjectいいえm に転送されるその他の属性。
childrenArray<Vnode>|String|Number|Booleanいいえこのリンクの子 vnode。
戻り値Vnodevnode。

m.route.param ​

最後に完全に解決されたルートからルートパラメータを取得します。ルートパラメータは、キーと値の組み合わせです。ルートパラメータは、いくつかの異なる場所から取得されます。

  • ルートの補間(例:ルートが /users/:id で、/users/1 に解決される場合、ルートパラメータにはキー id と値 "1" があります)
  • ルーターのクエリ文字列(例:パスが /users?page=1 の場合、ルートパラメータにはキー page と値 "1" があります)
  • history.state(例:history.state が {foo: "bar"} の場合、ルートパラメータにはキー foo と値 "bar" があります)

value = m.route.param(key)

引数型必須説明
keyStringいいえルートパラメータ名(例:ルート /users/:id の id、またはパス /users/1?page=3 の page、または history.state のキー)
戻り値String|Object指定されたキーの値を返します。キーが指定されていない場合は、すべての補間キーを含むオブジェクトを返します

RouteResolver の onmatch 関数では、新しいルートはまだ完全に解決されておらず、m.route.param() は、もしあれば、前のルートのパラメータを返すことに注意してください。onmatch は、新しいルートのパラメータを引数として受け取ります。

m.route.SKIP ​

ルートリゾルバーの onmatch から返され、次のルートにスキップするための特別な値。

RouteResolver ​

RouteResolver は、onmatch メソッド、render メソッド、またはその両方を含む、コンポーネントではないオブジェクトです。両方のメソッドはオプションですが、少なくとも1つは存在する必要があります。

オブジェクトがコンポーネントとして認識できる場合(view メソッドの存在、または function/class であることによって)、onmatch メソッドまたは render メソッドがあっても、コンポーネントとして扱われます。RouteResolver はコンポーネントではないため、ライフサイクルメソッドはありません。

経験則として、RouteResolver は m.route 呼び出しと同じファイルに配置し、コンポーネントの定義は独自のモジュールに配置すべきです。

routeResolver = {onmatch, render}

コンポーネントを使用する場合、コンポーネントが Home であると仮定すると、これらをこのルートリゾルバーの特別な糖衣構文と考えることができます。

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

routeResolver.onmatch ​

onmatch フックは、ルーターがレンダリングするコンポーネントを決定する必要があるときに呼び出されます。ルーターパスが変更されるごとに1回呼び出されますが、同じパスにおける後続の再描画では呼び出されません。コンポーネントの初期化前にロジックを実行するために使用できます(例:認証ロジック、データプリロード、リダイレクト分析トラッキングなど)。

このメソッドを使用すると、非同期的にレンダリングされるコンポーネントを定義できます。コンポーネントを非同期的にレンダリングするには、コンポーネントに解決される Promise を返します。

onmatch の詳細については、高度なコンポーネント解決セクションを参照してください。

routeResolver.onmatch(args, requestedPath, route)

引数型説明
argsObjectルーティングパラメータ
requestedPathString最後のルーティングアクションによって要求されたルーターパス。補間されたルーティングパラメータ値を含みますが、プレフィックスは含みません。onmatch が呼び出されると、このパスの解決処理は完了しておらず、m.route.get() は依然として前のパスを返します。
routeString最後のルーティングアクションによって要求されたルーターパス。補間されたルーティングパラメータ値を除きます。
戻り値Component|\Promise<Component>|undefinedコンポーネントまたはコンポーネントに解決される Promise を返します

onmatch がコンポーネントまたはコンポーネントに解決される Promise を返す場合、このコンポーネントは RouteResolver の render メソッドの最初の引数の vnode.tag として使用されます。それ以外の場合、vnode.tag は "div" に設定されます。同様に、onmatch メソッドが省略された場合、vnode.tag も "div" になります。

onmatch が拒否された Promise を返す場合、ルーターは defaultRoute にリダイレクトします。Promise チェーンで .catch を呼び出すことで、この動作をオーバーライドできます。

routeResolver.render ​

render メソッドは、一致するルートの再描画ごとに呼び出されます。コンポーネントの view メソッドに似ており、コンポーネントの合成を簡素化するために存在します。また、Mithril.js がサブツリー全体を置き換えるという通常の動作から逸脱することもできます。

vnode = routeResolver.render(vnode)

引数型説明
vnodeObject属性オブジェクトにルーティングパラメータを含むvnode。onmatch がコンポーネントまたはコンポーネントに解決される Promise を返さない場合、vnode の tag フィールドはデフォルトで "div" になります
vnode.attrsObjectURL パラメータ値のマップ
戻り値Array<Vnode>|Vnodeレンダリングされるvnode

vnode パラメータは、m(Component, m.route.param()) と同じです。ここで、Component はルートの解決済みコンポーネント(routeResolver.onmatch の後)であり、m.route.param() はこちらで説明されているとおりです。このメソッドを省略した場合、デフォルトの戻り値は [vnode] であり、key パラメータを使用できるようにフラグメントでラップされます。:key パラメータと組み合わせると、単一要素のキー付きフラグメントになります。これは、[m(Component, {key: m.route.param("key"), ...})] のようにレンダリングされるためです。

仕組み ​

ルーティングは、シングルページアプリケーション(SPA)を構築するためのシステムです。つまり、ブラウザを完全にリフレッシュすることなく、「ページ」から別のページに遷移できるアプリケーションです。

各ページを個別にブックマークする機能と、ブラウザの履歴メカニズムを介してアプリケーション内をナビゲートする機能を維持しながら、シームレスなナビゲーションを可能にします。

ページをリフレッシュしないルーティングは、history.pushState API によって部分的に実現されます。この API を使用すると、ページがロードされた後にブラウザに表示される URL をプログラムで変更できますが、コールド状態(例:新しいタブ)から特定の URL にナビゲートした場合に、適切なマークアップがレンダリングされるようにするのは、アプリケーション開発者の責任です。

ルーティング戦略 ​

ルーティング戦略は、ライブラリが実際にルーティングをどのように実装するかを規定します。SPA ルーティングシステムを実装するために使用できる一般的な戦略は3つあり、それぞれに異なる注意点があります。

  • m.route.prefix = '#!' (デフォルト) – URL のフラグメント識別子(ハッシュとも呼ばれます)の部分を使用します。この戦略を使用する URL は、通常 https://localhost/#!/page1 のようになります。
  • m.route.prefix = '?' – クエリ文字列を使用します。この戦略を使用する URL は、通常 https://localhost/?/page1 の形式になります。
  • m.route.prefix = '' – パス名を使用します。この戦略を使用する URL は、通常 https://localhost/page1 のようになります。

ハッシュ戦略を使用すると、history.pushState をサポートしていないブラウザでも動作することが保証されます。これは、onhashchange の使用にフォールバックできるためです。ハッシュを完全にローカルに保持する場合は、この戦略を使用してください。

クエリ文字列戦略を使用すると、サーバー側での検出が可能ですが、通常のパスとしては表示されません。アンカー付きリンクをサーバー側でサポートし、潜在的に検出したいものの、パス名戦略をサポートするために必要な変更を加えることができない場合(Apache を使用していて、.htaccess を変更できない場合など)は、この戦略を使用してください。

パス名戦略は、最もクリーンな URL を生成しますが、アプリケーションがルーティングできるすべての URL からシングルページアプリケーションコードを提供するようにサーバーを設定する必要があります。よりクリーンな URL が必要な場合は、この戦略を使用してください。

ハッシュ戦略を使用するシングルページアプリケーションは、ハッシュをアンカーへのリンクの目的ではなく、ルーティングメカニズムとして使用していることを示すために、ハッシュの後に感嘆符を付けることが一般的です。#! 文字列は、_ハッシュバン_として知られています。

デフォルトの戦略ではハッシュバンを使用します。

一般的な使用法 ​

通常、ルートをマッピングするために、いくつかのコンポーネントを作成する必要があります。

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

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

上記の例では、Home と Page1 の2つのコンポーネントがあります。それぞれにメニューといくつかのテキストが含まれています。メニュー自体は、繰り返しを避けるためにコンポーネントとして定義されています。

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

これで、ルートを定義し、コンポーネントをそれらにマッピングできます。

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

ここでは、/ と /page1 の2つのルートを指定します。これにより、ユーザーが各 URL にナビゲートすると、それぞれのコンポーネントがレンダリングされます。

異なるルートへのナビゲート ​

上記の例では、Menu コンポーネントに2つの m.route.Link があります。これにより、要素(デフォルトでは <a>)が作成され、ユーザーがクリックすると、別のルートにナビゲートするように設定されます。リモートにナビゲートするのではなく、ローカルにナビゲートします。

m.route.set(route) を使用して、プログラムでナビゲートすることもできます。たとえば、m.route.set("/page1") のようにします。

ルート間をナビゲートする場合、ルータープレフィックスは自動的に処理されます。つまり、Mithril.js ルートをリンクする場合は、m.route.set と m.route.Link の両方で、ハッシュバン #!(または m.route.prefix に設定したプレフィックス)を省略します。

コンポーネント間を遷移する場合、サブツリー全体が置き換えられることに注意してください。サブツリーをパッチするだけなら、render メソッドを持つルートリゾルバーを使用してください。

ルーティングパラメータ ​

場合によっては、変数 ID または同様のデータをルートに表示したいが、可能なすべての ID に対して個別のルートを明示的に指定したくない場合があります。これを実現するために、Mithril.js はパラメータ化されたルートをサポートしています。

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

上記の例では、ルート /edit/:id を定義しました。これにより、/edit/ で始まり、その後に何らかのデータが続く URL(例:/edit/1、edit/234 など)に一致する動的ルートが作成されます。次に、id 値はコンポーネントの vnode の属性(vnode.attrs.id)としてマッピングされます。

ルートに複数の引数を含めることができます。たとえば、/edit/:projectID/:userID は、コンポーネントの vnode 属性オブジェクトにプロパティ projectID と userID を生成します。

Key パラメータ ​

ユーザーがパラメータ化されたルートから、異なるパラメータを持つ同じルートに遷移する場合(例:ルート /page/:id が与えられた場合に /page/1 から /page/2 に移動する場合)、両方のルートが同じコンポーネントに解決されるため、コンポーネントは最初から再作成されず、仮想 DOM 内でインプレースな差分が発生します。これには、oninit/oncreate ではなく onupdate フックがトリガーされるという副作用があります。ただし、開発者がコンポーネントの再作成をルート変更イベントに同期させたいと思うのは比較的一般的です。

これを実現するために、ルートパラメータ化をkeyと組み合わせて、非常に便利なパターンを作成できます。

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

これは、ルートのルートコンポーネントに対して作成される vnode に、ルートパラメータオブジェクト key が存在することを意味します。ルートパラメータは vnode の attrs に格納されます。したがって、あるページから別のページにジャンプすると、key が変更され、コンポーネントが最初から再作成されます(key によって仮想 DOM エンジンは、古いコンポーネントと新しいコンポーネントが異なるエンティティであると判断します)。

そのアイデアをさらに発展させて、リロード時に自身を再作成するコンポーネントを作成できます。

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

または、履歴状態機能を使用して、URL を汚染せずにリロード可能なコンポーネントを実現することもできます。

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

key パラメータは、コンポーネントルートでのみ機能することに注意してください。ルートリゾルバーを使用している場合は、key: m.route.param("key") を渡して、単一要素のキー付きフラグメントを使用する必要があります。

可変長ルート ​

可変長ルート、つまりスラッシュを含む URL パス名を含む引数を持つルートを持つことも可能です。

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

404 の処理 ​

同型/ユニバーサル JavaScript アプリケーションの場合、URL パラメータと可変長ルートの組み合わせは、カスタム 404 エラーページを表示するのに非常に役立ちます。

404 Not Found エラーが発生した場合、サーバーはカスタムページをクライアントに送り返します。Mithril.js がロードされると、ルートが不明なため、クライアントはデフォルトルートにリダイレクトされます。

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

履歴状態 ​

基盤となる history.pushState API を最大限に活用して、ユーザーの遷移エクスペリエンスを向上させることができます。たとえば、アプリケーションは、ユーザーがナビゲートしてページを離れるときに、大きなフォームの状態を「記憶」することができます。これにより、ユーザーがブラウザの戻るボタンを押すと、空白のフォームではなく、フォームが入力された状態になります。

たとえば、次のようなフォームを作成できます。

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

このようにして、ユーザーが検索して戻るボタンを押してアプリケーションに戻ると、入力フィールドには検索語が入力されたままになります。この手法は、大きなフォームや、永続化されていない状態をユーザーが作成するのが面倒な他のアプリケーションといったケースで、ユーザーエクスペリエンスを向上させることができます。

ルータープレフィックスの変更 ​

ルータープレフィックスは、ルーターが使用する基盤となる戦略を規定する URL の一部です。

javascript
// パス名戦略に設定
m.route.prefix = '';

// クエリ文字列戦略に設定
m.route.prefix = '?';

// バンなしのハッシュに設定
m.route.prefix = '#';

// ルート以外の URL でパス名戦略に設定
// 例:アプリが `https://localhost/my-app` に存在し、別のものが
// `https://localhost` に存在する場合
m.route.prefix = '/my-app';

アドバンストコンポーネントの解決 ​

コンポーネントをルートに対応付ける代わりに、RouteResolverオブジェクトを指定できます。RouteResolverオブジェクトは、onmatch()メソッドおよび/またはrender()メソッドを含みます。両方のメソッドは任意ですが、少なくとも1つは必須です。

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

RouteResolverは、様々な高度なルーティングのユースケースを実装するのに役立ちます。

レイアウトコンポーネントのラップ ​

ルーティングされたコンポーネントの多くを、再利用可能なラッパー(一般的に「レイアウト」と呼ばれる)でラップしたい場合があります。これを行うには、まず、さまざまなコンポーネントをラップする共通のマークアップを含むコンポーネントを作成する必要があります。

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

上記の例では、レイアウトはコンポーネントに渡された子を含む<div class="layout">のみで構成されていますが、実際のシナリオでは、必要に応じて複雑にすることができます。

レイアウトをラップする1つの方法は、ルートマップで匿名コンポーネントを定義することです。

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

ただし、トップレベルのコンポーネントは匿名コンポーネントであるため、/ルートから/formルート(またはその逆)に遷移すると、匿名コンポーネントが破棄され、DOMが一から再作成されることに注意してください。Layoutコンポーネントにライフサイクルメソッドが定義されている場合、oninitおよびoncreateフックはルートが変更されるたびに実行されます。アプリケーションによっては、これが望ましい場合と望ましくない場合があります。

Layoutコンポーネントを最初から再作成するのではなく、差分処理を行い、そのまま維持したい場合は、代わりにRouteResolverをルートオブジェクトとして使用する必要があります。

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

この場合、Layoutコンポーネントにoninitおよびoncreateライフサイクルメソッドがある場合、最初のルート変更でのみ実行されます(すべてのルートが同じレイアウトを使用していると仮定)。

2つの例の違いを明確にするために、例1は次のコードと同等です。

javascript
// 機能的には例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);
    },
  },
});

Anon1とAnon2は異なるコンポーネントであるため、それらのサブツリー(Layoutを含む)は最初から再作成されます。これは、コンポーネントがRouteResolverなしで直接使用される場合にも発生します。

例2では、Layoutは両方のルートのトップレベルコンポーネントであるため、LayoutコンポーネントのDOMは差分検出されます(つまり、変更がない場合はそのまま残ります)。HomeからFormへの変更のみが、DOMのそのサブセクションの再作成をトリガーします。

リダイレクト ​

RouteResolverのonmatchフックを使用すると、ルートのトップレベルコンポーネントが初期化される前にロジックを実行できます。Mithrilのm.route.set()またはネイティブHTMLのhistory APIを使用することもできます。history APIを使用してリダイレクトする場合、onmatchフックは、一致するルートの解決を防ぐために、決して解決されないPromiseを返す必要があります。m.route.set()は、一致するルートの解決を内部的にキャンセルするため、これを使用する必要はありません。

例:認証 ​

以下の例は、ユーザーがログインしないと/secretページを表示できないようにするログインウォールを実装する方法を示しています。

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

アプリケーションがロードされると、onmatchが呼び出され、isLoggedInがfalseであるため、アプリケーションは/loginにリダイレクトされます。ユーザーがログインボタンを押すと、isLoggedInがtrueに設定され、アプリケーションは/secretにリダイレクトされます。onmatchフックが再度実行され、今回はisLoggedInがtrueであるため、アプリケーションはHomeコンポーネントをレンダリングします。

簡単にするために、上記の例では、ユーザーのログインステータスはグローバル変数に保持され、ユーザーがログインボタンをクリックすると、そのフラグが切り替えられるだけです。実際のアプリケーションでは、ユーザーは適切なログイン資格情報を提供する必要があり、ログインボタンをクリックすると、サーバーへのリクエストがトリガーされてユーザーが認証されます。

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

データのプリロード ​

通常、コンポーネントは初期化時にデータをロードできます。この方法でデータをロードすると、コンポーネントが2回描画されます。最初の描画パスはルーティング時に発生し、2番目の描画パスはリクエストが完了した後に発生します。loadUsers()がPromiseを返すことに注意してください。ただし、oninitによって返されるPromiseは現在無視されます。2番目の描画パスは、m.requestのbackgroundオプションによるものです。

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

上記の例では、最初レンダリングでは、リクエストが完了する前にstate.usersが空の配列であるため、UIに"loading"が表示されます。次に、データが利用可能になると、UIが再描画され、ユーザーIDのリストが表示されます。

RouteResolverは、コンポーネントのレンダリング前にデータをプリロードすることで、UIのちらつきを防ぎ、ローディングインジケータを不要にするメカニズムとして利用できます。

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

上記では、renderはリクエストが完了した後にのみ実行されるため、三項演算子は不要になります。

コードスプリッティング ​

大規模なアプリケーションでは、各ルートのコードを事前にダウンロードするのではなく、必要に応じてオンデマンドでダウンロードしたい場合があります。この方法でコードベースを分割することは、コードスプリッティングまたは遅延ロードと呼ばれます。Mithril.jsでは、これはonmatchフックからPromiseを返すことによって実現できます。

最も基本的な形式では、次のようになります。

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

ただし、現実的には、それが実際の運用環境で機能するためには、Home.jsモジュールのすべての依存関係を、最終的にサーバーによって提供されるファイルにバンドルする必要があります。

幸いなことに、遅延ロード用のモジュールをバンドルするタスクを容易にするツールが多数あります。以下は、多くのバンドラーでサポートされているネイティブ動的import(...)を使用した例です。

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

タイプ付きルート ​

特定の高度なルーティングのケースでは、パスだけでなく、数値IDのように値をさらに制約したい場合があります。ルートからm.route.SKIPを返すことで、それを簡単に行うことができます。

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

隠しルート ​

まれな状況では、一部のユーザーに対して特定のルートを非表示にしたい場合があります。例えば、特定のユーザーの表示が禁止されている場合に、権限エラーを表示する代わりに、存在しないものとして扱い、404ビューにリダイレクトする、といったことが考えられます。この場合、m.route.SKIPを使用して、ルートが存在しないかのように振る舞わせることができます。

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

ルートのキャンセル/ブロック ​

RouteResolverのonmatchは、決して解決されないPromiseを返すことで、ルートの解決を阻止できます。これは、冗長なルート解決の試みを検出し、キャンセルするために使用できます。

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

サードパーティの統合 ​

特定の状況では、Reactのような別のフレームワークと相互運用する必要がある場合があります。その方法を次に示します。

  • 通常どおりm.routeを使用してすべてのルートを定義しますが、_一度だけ_使用してください。複数のルートポイントはサポートされていません。
  • ルーティングのサブスクリプションを削除したい場合は、m.mount(root, null)を使用し、m.route(root, ...)で使用したのと同じルートを指定します。m.routeは内部的にm.mountを使用してすべてを接続するため、特別な処理ではありません。

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

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
前のページmount(root, component)
次のページrequest(options)

MITライセンス の下で公開されています。

Copyright (c) 2024 Mithril Contributors

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

MITライセンス の下で公開されています。

Copyright (c) 2024 Mithril Contributors