m.route



ラウティング(Routing = アメリカ英語読みはルーティングよりもラウティングが近い)は、シングルページアプリケーション(SPA)を作るための仕組みです。他のページに行く時に、フルにブラウザをリフレッシュしなくても済むアプリケーションを実現することができます。

この機能を使うと、各ページをブックマークしたり、ブラウザの履歴の機能はそのままに、シームレスなナビゲーションが可能になります。

このメソッドは4つの異なる機能をオーバーロードしています:

  • m.route(rootElement, defaultRoute, routes) - アプリケーションで使用できるURLとその時にロードされるコンポーネントの定義

  • m.route(path) - 他のラウトへリダイレクト

  • m.route() - 現在アクティブなラウトの取得

  • m.route(element) - ラウトのモードを抽象化し、現在のモードで利用可能なエレメントの実リンクを取得

ラウトはシングルページアプリケーション(SPA)を実現しやすくする仕組みで、 location.hash、HTML5のURL書き換え、location.querystringのどれかの方法を使って実装できます。それぞれの実装方法の詳細については、m.route.modeを参照してください。


ラウトの定義

使用方法

ラウトを定義する時は、ホストとなるDOM要素と、デフォルトのラウト、あとは遷移する可能性のあるラウトとそれをレンダリングするためのコンポーネントのキー・バリューのマップが必要となります。ラウトのリストを定義した場合は、モジュールの初期化をするためにm.mountを呼ぶ必要はありません。m.routeが代わりに呼んでくれます。

次のサンプルは、<body>のレンダリングをする、3つのラウトを定義しています。homelogindashboardはそれぞれコンポーネントです。どのようにコンポーネントを定義するか見ていきましょう。

m.route(document.body, "/", {
    "/": Home,
    "/login": Login,
    "/dashboard": Dashboard,
});

:を前に付いた単語を書くことで、ラウトに引数を設定することができます。

次のサンプルはuserIDパラメータを取るラウトです。

//サンプルコンポーネント
var Dashboard = {
    controller: function() {
        return {id: m.route.param("userID")}
    },
    view: function(controller) {
        return m("div", controller.id);
    }
}

//#記号から始まるラウトを使用するための設定
m.route.mode = "hash";

//ラウトの定義
m.route(document.body, "/dashboard/johndoe", {
    "/dashboard/:userID": Dashboard
});

これを実行すると、http://server/#/dashboard/johndoeにリダイレクトして、下記のタグを挿入します:

<body><div>johndoe</div></body>

上記のサンプルのdashboardはコンポーネントです。モジュールはcontrollerプロパティとviewプロパティを持ちます。URLがラウトにマッチすると、対応するコンポーネントのコントローラがインスタンス化されて、引数としてビューに渡されます。

この場合、ひとつのラウトしかないため、アプリケーションはデフォルトのラウトの"/dashboard/johndoe"にリダイレクトされます。。

johndoeという文字列が:userIDパラメータに結び付けられます。このパラメータは、コントローラ内でm.route.param("userID")というAPI呼び出しをするとプログラムから参照できます。

m.route.modeは、URLのどの部分をラウトとして使うかを設定します。


可変個引数のラウト

省略記号(...)をラウトの引数の名前に付けると、スラッシュを含むURLにマッチさせることができます:

m.route(document.body, "/files/pictures/pic1.jpg", {
    "/files/:file...": gallery
});

m.route.param("file") === "pictures/pic1.jpg"
m.route(document.body, "/blog/2014/01/20/articles", {
    "/blog/:date.../articles": articleList
});

m.route.param("date") === "2014/01/20"

Mithrilはラウトのマッチを定義された順番に行っていきます。そのため、可変個の引数を持つラウトは末尾に書かないと、より範囲の狭いラウトのマッチが行われなくなります。

m.route(document.body, "/blog/archive/2014", {
    "/blog/:date...": Component1, //上記のデフォルトパスはこのラウトに先にマッチします!
    "/blog/archive/:year": Component2
});

m.route.param("date") === "archive/2014"

//`m.route.param("year") == "2014"`が成立するように、ラウト定義の順番を変えること

ラウトとクエリー文字列

ラウトパラメータ以外にも、クエリー文字列を使って任意のデータをm.route.paramに渡すことができます。

m.route("/grid?sortby=date&dir=desc")

var sortBy = m.route.param("sortby") // "date"
var dir = m.route.param("dir") // "desc"

ラウト変更時に後処理コードを実行する

コンポーネントのコントローラがonunloadインスタンスメソッドを定義している場合、ラウトが変更されるとこのメソッドが呼び出されます。

var Home = {
    controller: function() {
        return {
            onunload: function() {
                console.log("ホームをアンロード");
            }
        };
    },
    view: function() {
        return m("div", "Home")
    }
};

var Dashboard = {
    controller: function() {},
    view: function() {}
};

//デフォルトラウト(home)に移動
m.route(document.body, "/", {
    "/": Home,
    "/dashboard": Dashboard,
});

//re-route to dashboard
m.route("/dashboard"); // logs "unloading home component"

This mechanism is useful to clear timers and unsubscribe event handlers. コンポーネントが階層化されている場合は、階層の中のモジュールのすべてのonunloadを呼ぶこともできますし、特定のコンポーネントをアンロードするときにはpubsubライブラリを呼ぶこともできます。


シグニチャ

シグニチャの読み方

void route(DOMElement rootElement, String defaultRoute, Object<Component> routes) { String mode, String param(String key), String buildQueryString(Object data), Object parseQueryString(String data) }

where:
    Component :: Object { void controller(), void view(Object controllerInstance) }
  • DOMElement root

    ビューのテンプレートの結果が作成されるDOMエレメント。

  • String defaultRoute

    現在のURLが、どの定義されたラウトにもマッチしなかった時にリダイレクトされラウト

  • Object routes

    利用可能なラウトと、そのラウトに対応するコンポーネントの対応が格納されたキー・バリュー・マップ。キーは絶対パスを指定しますが、動的パラメータを含めることができます。動的パラメータはコロン(:)で始まっている単語です。

    {'/path/to/page/': pageComponent} - 基本パス名のラウト

    {'/path/to/page/:id': pageComponent} - idと呼ばれる動的パラメータを含むパス名のラウトこのラウとは、/path/to/page/1/path/to/page/testといったURLが指定された場合に選択されます。

    {'/user/:userId/book/:bookId': userBookComponent} - 2つのパラメータを含むパス名のラウト

    動的パラメータは、URLパターンに応じてコンポーネント選択する時に使用できるワイルドカードです。URLの中で動的パラメータを置き換えた値は、m.route.param()を通じて取得することができます。

    ラウトの解決に使うURLの部位は、m.route.modeによって決定されます。デフォルトではラウト集に対してクエリー文字列がURLの構成要素として解釈されます。

    現在のページのURLが、設定されたラウトにマッチした場合には、対応するコンポーネントがアクティブになります。コンポーネントに関してはm.componentを参照してください。

  • m.route.mode

    String mode

    m.route.modeプロパティを使うと、どのURLに対してラウティングの仕組みを実装するかを定義できます。このプロパティには、"search"、"hash"、"pathname"のいづれかの文字列を設定できます。デフォルトは"search"です。この設定を変更する場合には、m.routeを呼び出す前に行ってください。

    • searchモードはクエリー文字列 (?以降)を利用します。このモードを使うと、名前付きのアンカー(例えば、<a href="#top">トップに戻る</a><a name="top"></a>)を使うことができますが、IE8の場合はhistory.pushStateのサポートがないため、ページリフレッシュが発生してしまいます。

      サンプルURL: http://server/?/path/to/page

    • hashモードはハッシュ(#以降)を使います。このモードは唯一、どのブラウザでもページリフレッシュが発生しません。しかし、このモードでは名前付きアンカーが使えなくなります。

      サンプルURL: http://server/#/path/to/page

    • pathnameモードは特別な文字を含まないURLを許可します。しかし、このモードでブックマークとページリフレッシュをサポートするためには、サーバ側にも手を加える必要があります。IE8上では、常にページリフレッシュが発生します。

      サンプルURL: http://server/path/to/page

      pathnameモードを使用するためのサーバ設定の中で、一番簡単な方法は、どのURLが要求されても同じコンテンツを返すようにする方法です。Apacheを使っている場合は、mod_rewriteを使用してURLの書き換えを行うことでできます。

      pathnameモードを使う場合は、アプリケーションがルートのURLで実行するようにしてください。

  • m.route.param

    String param(String key)

    ラウトのパラメータは、現在アクティブなラウトのシグニチャを元にして、現在のURLから取り出された動的な値です。

    パラメータのないラウトは次のようなものです:

    "/path/to/page/"

    パラメータ付きのラウトは次のような文字列です:

    "/path/to/page/:id" - ここでは、idがラウトパラメータの名前

    もし、現在アクティブなラウトが/dashboard/:userIDで、現在のURLが/dashboard/johndoeだとすると、m.route.param("userID")"johndoe"を返します。

    ラウト中のクエリー文字列のパラメータもこのコレクションに自動的に格納されます。

    "/grid?sortby=date" - ここでは、m.route.param("sortby")"date"を返す

    • String key

      ラウトパラメータの名前

    • returns String value

      keyにマップされたパラメータの値

    Object param()

    • returns Object params

      ラウトのパラメータをすべて含むオブジェクト

  • m.route.buildQueryString

    String buildQueryString(Object data)

    URI.jsと同じシリアライズ規約を使い、オブジェクトをURIでエンコードされたクエリー文字列にシリアライズします。

    • Object data

      シリアライズしたいオブジェクト

    • returns String querystring

      入力データのシリアライズ化表現

  • m.route.parseQueryString

    Object parseQueryString(String querystring)

    URI.jsのデシリアライズ規約を用いて、URIエンコードされたクエリー文字列表現されたオブジェクトをデシリアライズします。

    • String querystring

      デシリアライズするURIエンコーディングされたクエリー文字列

    • returns Object data

      デシリアライズしたオブジェクト


リダイレクト

使用方法

APIを使用して他のページにリダイレクトすることもできます。ラウトの定義のセクションのサンプルですでに使っています:

m.route("/dashboard/marysue");

このコードを実行すると、http://server/#/dashboard/marysueにリダイレクトします。


シグニチャ

シグニチャの読み方

void route(String path [, any params] [, Boolean shouldReplaceHistory])
  • String path

    リダイレクト先のラウト。もし、Mithrilのラウティングで取り扱っている範囲外のページにリダイレクトする場合は、window.locationを使うべきです。

  • any params

    クエリー文字列として渡されるパラメータ

  • Boolean shouldReplaceHistory

    もしこのパラメータがtrueに設定されると、新しいページを追加するのではなく、現在のヒストリのエントリーを置き換えます。デフォルトはfalseです。


現在アクティブなラウトの取得

使用方法

Mithrilはレンダリングの後に、ネイティブのlocationを更新して、history.pushState APIが正しい履歴のエントリーが表示されるようにします(Chromeの場合はCtrl+H page)。

コントローラ内で、現在アクティブなラウトを取得するには、m.route()を使います。この関数は、m.route.modeで決定されるURLのパーツ(マイナス?もしくは#シンボル - それぞれsearchモードとhash モード時)を返します。

//もしロケーションバーが"http://example.com/?/foo/bar"
//で、m.route.modeが`search`の場合は、
//`currentRoute == "/foo/bar"`
var currentRoute = m.route();

シグニチャ

シグニチャの読み方

String route()
  • returns String route

    現在アクティブなラウトを返します。


モードの抽象化

使用方法

このメソッドを使うには、 config仮想エレメントのプロパティを使用します。サンプル:

//`config`の設定を使うことで、`href`内に'#'を書かなくてもよくなる。
m("a[href='/dashboard/alicesmith']", {config: m.route});

この書き方を使用すると、どのm.route.modeが選択されていたとしても、期待通りの実行結果が得られます。href属性の中に?#をハードコードするのではなく、常に上記のようなイディオムを使うのが良いプラクティスです。

仮想エレメントについての詳細は、m()のドキュメントを参照してください。


シグニチャ

シグニチャの読み方

void route(DOMElement element, Boolean isInitialized, Object context, Object vdom)
  • DOMElement element

    ラウトを指すhref属性を持つ<a>エレメント。

  • Boolean isInitialized

    このフラグがtrueの場合はこのメソッドは実行されません。これは、仮想DOMエレメントconfig属性に対して、メソッドの互換性を維持するために使用します。m()を参照して下さい。

  • Object context

    再描画間で状態を保持するオブジェクトです。

  • Object vdom

    configが適用された仮想DOMデータ構造