ルート

ルートの定義

まず、Framework7 アプリを初期化するときは、`routes` 配列パラメータを使用してデフォルトのルートを渡す必要があります

var app = new Framework7({
  routes: [
    {
      name: 'about',
      path: '/about/',
      url: './pages/about.html',
    },
    {
      name: 'news',
      path: '/news/',
      url: './pages/news.html',
      options: {
        animate: false,
      },
    },
    {
      name: 'users',
      path: '/users/',
      componentUrl: './pages/users.html',
      options: {
        props: {
          users: ['John Doe', 'Vladimir Kharlampidi', 'Timo Ernst'],
        },
      },
      on: {
        pageAfterIn: function test (e, page) {
          // do something after page gets into the view
        },
        pageInit: function (e, page) {
          // do something when page initialized
        },
      }
    },
    // Default route, match to all pages (e.g. 404 page)
    {
      path: '(.*)',
      url: './pages/404.html',
    },
  ],
});

アプリの初期化時に定義されたルートはデフォルトのルートであり、アプリ内の任意の View/Router で使用できます。

マルチビュー/ルーターアプリがあり、一部の View/Router に**独自の厳密なルート**を設定し、この View でデフォルトのルートを使用できないようにするには、View の初期化時に同じ `routes` パラメータを指定できます

var view1 = app.views.create('.view-1', {
  routes: [
    {
      path: '/users/',
      url: './pages/users.html',
    },
    {
      path: '/user/',
      url: './pages/user.html',
    },
  ],
});

マルチビュー/ルーターアプリがあり、一部の View/Router に**追加のルート**を設定し、これらの追加のルートを他の View で使用できないようにするには、View の初期化時に `routesAdd` パラメータを指定できます

// This view will support all global routes + own additional routes
var view2 = app.views.create('.view-2', {
  // These routes are only available in this view
  routesAdd: [
    {
      path: '/blog/',
      url: './pages/blog.html',
    },
    {
      path: '/post/',
      url: './pages/post.html',
    },
  ],
})

ルートのプロパティ

それでは、各ルートプロパティの意味を見ていきましょう

パラメータタイプ説明
name文字列ルート名、例:`home`
path文字列ルートパス。このパスに一致するリンクをクリックするか、API を使用してこのパスでロードすると、このルートがロードされます。
optionsオブジェクト追加のルートオプションを含むオブジェクト (オプション)
routes配列ネストされたルートの配列
viewName文字列このルートの読み込みが強制される View 名
マスター詳細
master真偽値
function(app, router)
このルートをマスタールートとして有効にします。また、`app` とルーターインスタンスを受け取るメソッドにすることもできます。このメソッドでは、`true` または `false` を返す必要があります
{
  url: '/some-page/',
  master(app) {
    if (app.device.desktop) return true;
    return false;
  }
}
detailRoutes配列詳細ルートの配列
遅延読み込みモジュール
modules配列ルートの読み込み前に読み込む遅延読み込みモジュールの配列
コンテンツ関連のプロパティ
次のルートプロパティは、コンテンツをどのように (どこから/何を) 読み込むかを定義します
content文字列指定されたコンテンツ文字列から動的ページを作成します
url文字列Ajax を介してページコンテンツを読み込みます。

また、`{{paramName}}` 式を使用して、ルートパス からの動的なルートパラメータもサポートします。例:

{
  path: '/users/:userId/posts/:postId',
  url: 'http://myapp.com/posts/{{userId}}/{{postId}}'
}
componentオブジェクト渡された Framework7 ルーターコンポーネント からページを読み込みます
componentUrl文字列Ajax を介してページをコンポーネントとして読み込みます

また、`{{paramName}}` 式を使用して、ルートパス からの動的なルートパラメータもサポートします

asyncfunction(context)必要な非同期操作を実行し、必要なルートコンテンツとオプションを返します。引数として、ルートコールバックコンテキスト オブジェクトを受け取ります。
asyncComponentfunction()

メソッドは、コンポーネント、またはコンポーネントを含む `.default` プロパティを持つ ES モジュールで解決された Promise を返す必要があります。

これは主に、コンポーネントを動的にインポートするための `async` の短いバージョンとして設計されています。例:

{
  path: '/some-page/',
  asyncComponent: () => import('./path/to/some-page.js'),
}
ルーティング可能なタブ
tabs配列タブ route の配列
ルーティング可能なモーダル
actionsオブジェクトアクションシート route
popupオブジェクトポップアップ route
loginScreenオブジェクトログイン画面 route
popoverオブジェクトポップオーバー route
sheetオブジェクトシート route
ルーティング可能なパネル
panelオブジェクトパネル route
イベント
onオブジェクトイベントハンドラを含むオブジェクト
エイリアスとリダイレクト
alias文字列
配列
ルートエイリアス、またはルートエイリアスの配列。ここではエイリアス **パス** を指定する必要があります
redirect文字列
function(context)
ルートリダイレクト。ここではリダイレクト **URL** (パスではない) を指定する必要があります。メソッドの場合は、引数として ルートコールバックコンテキスト オブジェクトを受け取ります。
入る前/出る前
beforeEnterfunction(context)

配列
ルートの読み込み/進入前に実行される関数 (または関数の配列)。ルートの読み込みを続行するには、`resolve` を呼び出す必要があります。`array` の場合、配列内のすべての関数を解決して続行する必要があります。メソッドの場合は、引数として ルートコールバックコンテキスト オブジェクトを受け取ります。
beforeLeavefunction(context)

配列
ルートのアンロード/離脱前に実行される関数 (または関数の配列)。ナビゲーションを続行するには、`resolve` を呼び出す必要があります。`array` の場合、配列内のすべての関数を解決して続行する必要があります。メソッドの場合は、引数として ルートコールバックコンテキスト オブジェクトを受け取ります。
keepAlive
keepAlive真偽値いわゆる `keepAlive` ルートを有効にします。有効にすると、一度読み込まれたページとそのコンポーネント (Vue、React、または Router コンポーネント) は破棄されません。代わりに、DOM からデタッチされ、必要に応じて再利用されます。

考えられるほとんどのオプションの例を次に示します

routes: [
  // Load via Ajax
  {
    path: '/about/',
    url: './pages/about.html',
  },
  // Dynamic page from content
  {
    path: '/news/',
    content: `
      <div class="page">
        <div class="page-content">
          <div class="block">
            <p>This page created dynamically</p>
          </div>
        </div>
      </div>
    `,
  },
  // By page name (data-name="services") presented in DOM
  {
    path: '/services/',
    pageName: 'services',
  },
  // By page HTMLElement
  {
    path: '/contacts/',
    el: document.querySelector('.page[data-name="contacts"]'),
  },
  // By component
  {
    path: '/posts/',
    component: {
      // look below
    },
  },
  // By component url
  {
    path: '/post/:id/',
    componentUrl: './pages/component.html',
  },
  // Async
  {
    path: '/something/',
    async: function ({ app, to, resolve }) {
      // Requested route
      console.log(to);
      // Get external data and return page content
      fetch('http://some-endpoint/')
        .then((res) => res.json())
        .then(function (data) {
          resolve(
            // How and what to load
            {
              content: `<div class="page">${data.users}</div>`
            },
          );
        });
      }
  }
],

ルートパス

上記のように、ルートの `path` プロパティは、API によって、または同じパスを持つリンクをクリックすることによって、次のルートが読み込まれたときにブラウザウィンドウのアドレスバーに表示されるパス/ URL を意味します ( `browserHistory` が有効になっている場合)。

動的パスもサポートされています。ルートに `/blog/users/:userId/posts/:postId/` というパスがあり、`/blog/users/12/posts/25` href のリンクをクリックすると、読み込まれたページで `route.params` オブジェクトにアクセスできます。これには `{ userId: 12, postId: 25 }` が含まれています

ルートパスのマッチングは、Path To Regexp ライブラリによって処理されるため、そこでサポートされているものはすべて Framework7 でもサポートされています。たとえば、すべてのパスのデフォルトルートを追加する場合は、次のような正規表現を使用できます

// Default route, match to all pages (e.g. 404 page)
{
  path: '(.*)',
  url: './pages/404.html',
},

ルートオプション

`options` プロパティに渡すことができる追加のルートオプションを見てみましょう

パラメータタイプ説明
animate真偽値ページをアニメーション化するかどうか (デフォルトのルーター設定を上書きします)
history真偽値ページをルーター履歴に保存するかどうか
browserHistory真偽値ページをブラウザの状態に保存するかどうか。`browserHistory` を使用している場合は、ルートがブラウザ履歴に保存されないように、ここで `false` を渡すことができます
reloadCurrent真偽値現在のページをルートからの新しいページに置き換えます。この場合はアニメーションはありません
reloadPrevious真偽値履歴の前のページをルートからの新しいページに置き換えます
reloadAll真偽値新しいページを読み込み、履歴と DOM から以前のすべてのページを削除します
clearPreviousHistory真偽値指定されたルートにリロード/移動した後、以前のページ履歴がクリアされます
ignoreCache真偽値`true` に設定されている場合、キャッシュにそのような URL があるかどうかを無視し、XHR を使用して再度リロードします
force真偽値`true` に設定されている場合、履歴の前のページを無視し、指定されたページを読み込みます
propsオブジェクトVue/React ページコンポーネント props として渡される props
transition文字列カスタムページ遷移名
openIn文字列ページルートをモーダルまたはパネルとして開くことができます。したがって、次のいずれかになります:`popup`、`popover`、`loginScreen`、`sheet`、`panel`

ルートコールバックコンテキスト

`async`、`redirect`、`beforeEnter`、`beforeLeave` ルートプロパティで使用されるルートコンテキストコールバックの形式

プロパティ
appグローバルアプリインスタンスへのリンク
toリクエストされたルート
from現在アクティブなルート
router現在の Router インスタンス
resolveルーティングを解決/続行するために呼び出すメソッド
rejectルーティングを拒否/防止するために呼び出すメソッド
directionナビゲーション方向。`forward` または `backward` になります

非同期ルート

`async` ルートプロパティは、動的なルートプロパティを返すように設計された非常に強力なツールです。これは、次の引数を持つ関数です

ルートコールバックの `resolve` メソッドは、次の形式です

resolve(parameters, options)

  • parameters オブジェクト - 解決されたルートコンテンツを含むオブジェクト。`url`、`content`、`component`、または `componentUrl` プロパティのいずれかを含む必要があります
  • options オブジェクト - ルートオプション を含むオブジェクト

`reject` コールバック関数には引数はありません

reject()

非同期メソッドで resolve または reject を呼び出すまで、ルーティングはブロックされることに注意してください!

例:

routes = [
  {
    path: '/foo/',
    async({ resolve, reject }) {
      if (userIsLoggedIn) {
        resolve({ url: 'secured.html' })
      } else {
        resolve({ url: 'login.html' })
      }
    }
  }
]

ルートイベント

ルートの on プロパティを使用して、このページのルート内にすべてのページイベントを追加できます。例:

var app = new Framework7({
  routes: [
    // ...
    {
      path: '/users/',
      url: './pages/users.html',
      on: {
        pageBeforeIn: function (event, page) {
          // do something before page gets into the view
        },
        pageAfterIn: function (event, page) {
          // do something after page gets into the view
        },
        pageInit: function (event, page) {
          // do something when page initialized
        },
        pageBeforeRemove: function (event, page) {
          // do something before page gets removed from DOM
        },
      }
    },
    // ...
  ],
});

このようなルートイベントは実際にはDOMイベントであるため、各ハンドラーは最初の引数としてイベント自体を含む event を、2番目の引数としてページデータを含む page を受け入れることに注意してください。

また、このようなイベントハンドラーのコンテキスト(this)は、関連するRouterインスタンスを指します。

ネストされたルート

ネストされたルート(ルート内のルート)を持つことも可能です。

routes = [
  {
    path: '/faq/',
    url: './pages/faq.html',
  },
  {
    path: '/catalog/',
    url: './pages/catalog.html',
    routes: [
      {
        path: 'computers/',
        url: './pages/computers.html',
      },
      {
        path: 'monitors/',
        url: './pages/monitors.html',
      },
      ...
    ],
  }
];

これはどういう意味でしょうか?より理解を深めるために、実際には(内部的には)そのようなルートは以下のようにマージされます。

routes = [
  {
    path: '/faq/',
    url: './pages/faq.html',
  },
  {
    path: '/catalog/',
    url: './pages/catalog.html',
  }
  {
    path: '/catalog/computers/',
    url: './pages/computers.html',
  },
  {
    path: '/catalog/monitors/',
    url: './pages/monitors.html',
  },
];

たとえば、/catalog/ ページにいて、次のリンクがあるとします。

  1. <a href="computers/">Computers</a> - 期待通り動作します。リンクは現在のルート(/catalog/ + computers/)とマージされ、ルートにある /catalog/computers/ が得られます。

  2. <a href="./computers/">Computers</a> - パスの先頭の ./ は同じサブレベルを意味するため、ケース1と同じように動作します。

  3. <a href="/catalog/computers/">Computers</a> - 先頭の /(スラッシュ)はルートを意味するため、ケース1と同じように期待通り動作します。そして、マージされたルートにはそのようなルートルートがあります。

  4. <a href="/computers/">Computers</a> - 先頭の /(スラッシュ)はルートを意味するため、期待通り動作しません。そして、ルートにはそのような /computers/ ルートルートがありません。

詳細ルート

マスター詳細ビューの場合、マスタールートの master: true に加えて、detailRoutes を指定することもできます。

detailRoutes が指定されている場合、詳細ルートに移動すると、そのマスタールートもプリロードされます。

ただし、ネストされたルート(routes パラメーターで指定)とは異なり、詳細ルートの path はマスタールートの path とマージされません。

routes = [
  {
    path: '/blog/',
    url: './news.html',
    master: true,
    detailRoutes: [
      {
        /* We need to specify detail route path from root */
        path: '/blog/:postId/',
        url: './post.html',
      },
    ],
  },
  // ...
]

ルーティング可能なタブ

ルーティング可能なタブとはどういう意味で、なぜそれが良いのでしょうか?

まず、アプリのルートでタブのルートを指定する必要があります。/tabs/ ルートにルーティング可能なタブを持つページがあると仮定しましょう。

routes = [
  {
    path: '/about-me/',
    url: './pages/about-me/index.html',
    // Pass "tabs" property to route
    tabs: [
      // First (default) tab has the same url as the page itself
      {
        path: '/',
        id: 'about',
        // Fill this tab content from content string
        content: `
          <div class="block">
            <h3>About Me</h3>
            <p>...</p>
          </div>
        `
      },
      // Second tab
      {
        path: '/contacts/',
        id: 'contacts',
        // Fill this tab content via Ajax request
        url: './pages/about-me/contacts.html',
      },
      // Third tab
      {
        path: '/cv/',
        id: 'cv',
        // Load this tab content as a component via Ajax request
        componentUrl: './pages/about-me/cv.html',
      },
    ],
  }
]

/about-me/ ページでは、たとえば次のような構造になる場合があります。

<div class="page">
  <div class="navbar">
    <div class="navbar-bg"></div>
    <div class="navbar-inner">
      <div class="title">About Me</div>
    </div>
  </div>
  <div class="toolbar tabbar toolbar-bottom">
    <div class="toolbar-inner">
      <a href="./" class="tab-link" data-route-tab-id="about">About</a>
      <a href="./contacts/" class="tab-link" data-route-tab-id="contacts">>Contacts</a>
      <a href="./cv/" class="tab-link" data-route-tab-id="cv">>CV</a>
    </div>
  </div>
  <div class="tabs tabs-routable">
    <div class="tab page-content" id="about"></div>
    <div class="tab page-content" id="contacts"></div>
    <div class="tab page-content" id="cv"></div>
  </div>
</div>

通常のタブとほぼ同じですが、タブリンクとタブに tab-link-active クラスと tab-active クラスがなくなった点が異なります。これらのクラスとタブはルーターによって切り替えられます。そして、新しい data-route-tab-id 属性があります。これは、タブスイッチャーが選択されたルートに関連するリンクを理解するために必要です。

ルーティング可能なタブとその追加イベントの詳細については、タブコンポーネントページの該当するセクションを参照してください。

ルーティング可能なモーダル

モーダルもルーティング可能です。ここでいうモーダルとは、ポップアップポップオーバーアクションシートログイン画面シートモーダルなどのコンポーネントを指します。おそらく、ポップアップとログイン画面はここでより多くのユースケースを持っています。

そして、ルーティング可能なタブやページと同じ機能があります。

routes = [
  ...
  // Creates popup from passed HTML string
  {
    path: '/popup-content/',
    popup: {
      content: `
        <div class="popup">
          <div class="view">
            <div class="page">
              ...
            </div>
          </div>
        </div>
      `
    }
  },
  // Load Login Screen from file via Ajax
  {
    path: '/login-screen-ajax/',
    loginScreen: {
      url: './login-screen.html',
      /* login-screen.html contains:
        <div class="login-screen">
          <div class="view">
            <div class="page">
              ...
            </div>
          </div>
        </div>
      */
    },
  },
  // Load Popup from component file
  {
    path: '/popup-component/',
    loginScreen: {
      componentUrl: './popup-component.html',
      /* popup-component.html contains:
        <template>
          <div class="popup-screen">
            <div class="view">
              <div class="page">
                ...
              </div>
            </div>
          </div>
        </template>
        <style>...</style>
        <script>...</script>
      */
    },
  },
  // Use async route to check if the user is logged in:
  {
    path: '/secured-content/',
    async({ resolve }) {
      if (userIsLoggedIn) {
        resolve({
          url: 'secured-page.html',
        });
      } else {
        resolve({
          loginScreen: {
            url: 'login-screen.html'
          } ,
        });
      }
    },
  }
]

上記の例によると

ルーティング可能なパネル

ルーティング可能なパネルは、Framework7バージョン3.2.0から利用可能です。

パネル(サイドパネル)も、ルーティング可能なモーダルやページと同じ機能でルーティング可能です。

routes = [
  ...
  // Creates Panel from passed HTML string
  {
    path: '/left-panel/',
    panel: {
      content: `
        <div class="panel panel-left panel-cover">
          <div class="view">
            <div class="page">
              ...
            </div>
          </div>
        </div>
      `
    }
  },
  // Load Panel from file via Ajax
  {
    path: '/right-panel-ajax/',
    panel: {
      url: './right-panel.html',
      /* right-panel.html contains:
      <div class="panel panel-right panel-reveal">
        <div class="view">
          <div class="page">
            ...
          </div>
        </div>
      </div>
      */
    },
  },
  // Load Panel from component file
  {
    path: '/panel-component/',
    panel: {
      componentUrl: './panel-component.html',
      /* panel-component.html contains:
      <template>
        <div class="panel panel-left panel-cover">
          <div class="view">
            <div class="page">
              ...
            </div>
          </div>
        </div>
      </template>
      <style>...</style>
      <script>...</script>
      */
    },
  },
]

上記の例によると

ルーティング可能なパネルは静的パネルと混在できないことに注意してください。そのため、アプリに静的な左パネルがある場合、右パネルのみをルーティング可能なパネルとして読み込むことができます。

ルートに入る前/出る前

beforeEnter および beforeLeave ルートフックは、ルートの読み込み(入る)前とアンロード(出る)前に追加のチェックを実行したり、追加のアクションを実行したり、何かを読み込み/送信したりする必要がある場合に非常に役立ちます。単一のメソッドにすることも、実行されるメソッドの配列にすることもできます。例:

routes = [
  {
    path: 'profile',
    url: 'profile.html',
    beforeEnter: function ({ resolve, reject }) {
      if (/* some condition to check user is logged in */) {
        resolve();
      } else {
        // don't allow to visit this page for unauthenticated users
        reject();
      }
    },

  },
  {
    path: 'profile-edit',
    url: 'profile-edit.html',
    beforeLeave: function ({ resolve, reject }) {
      if (/* user didn't save edited form */) {
        app.dialog.confirm(
          'Are you sure you want to leave this page without saving data?',
          function () {
            // proceed navigation
            resolve();
          },
          function () {
            // stay on page
            reject();
          }
        )
      } else {
        resolve();
      }
    }
  }
]

もちろん、関数の配列として渡された場合は、複数のフックがサポートされます。

function checkAuth({ to, from, resolve, reject }) {
  if (/* some condition to check user is logged in */) {
    resolve();
  } else {
    reject();
  }
}
function checkPermission({ to, from, resolve, reject }) {
  if (/* some condition to check user edit permission */) {
    resolve();
  } else {
    reject();
  }
}

routes = [
  {
    path: '/profile/',
    url: 'profile.html',
    // check if the user is logged in
    beforeEnter: checkAuth,
  },
  {
    path: '/profile-edit/',
    url: 'profile-edit.html',
    // check if the user is logged in and has required permission
    beforeEnter: [checkAuth, checkPermission],
  }
]

ページが component プロパティを使用して読み込まれた場合、beforeEnter は正しく機能しません。そのような場合は、async または asyncComponent プロパティを使用して、beforeEnter がトリガーされた後にページを動的に読み込む必要があります。

リダイレクトとエイリアス

エイリアス

ルートの alias プロパティを使用してルートエイリアスを渡すことができます。この場合のエイリアスは、基本的に同じルートにアクセスするための複数のパスを持つことができることを意味します。

routes = [
  {
    path: '/foo/',
    url: 'somepage.html',
    alias: '/bar/',
  },
  {
    path: '/foo2/',
    url: 'anotherpage.html',
    alias: ['/bar2/', '/baz/', '/baz2/'],
  }
]

上記の例によると

リダイレクト

redirect プロパティを使用してルートリダイレクトを渡すことができます。

例:

routes = [
  {
    path: '/foo/',
    url: 'somepage.html',
  },
  {
    path: '/bar/',
    redirect: '/foo/',
  },
  {
    path: '/baz/',
    redirect: function ({to, resolve, reject}) {
      // if we have "user" query parameter
      if (to.query.user) {
        // redirect to such url
        resolve('/foo/?user=' + to.query.user);
      }
      // otherwise do nothing
      else reject();
    }
  }
]

上記の例は次のことを意味します。

リダイレクトでは、エイリアスのようなルートパスではなく、URLを渡すことに注意してください。

キープアライブ

keepAliveルートは、Framework7バージョン3.6.0から利用可能です。

keepAlive が有効になっている場合、ルーターがそのようなページを読み込むと、ページと、該当する場合はそのコンポーネント(Vue、React、またはルーターコンポーネント)は破棄されません。代わりに、DOMからデタッチされ、必要に応じて再利用されます。

あまり頻繁に更新されない「重い」ページを有効にするのに役立ちます。たとえば、マップのあるページ、重いキャンバスのあるページ、その他の計算のあるページなどです。通常のロジックでは、これらの重い計算は、このページにアクセスするたびに発生します。ただし、keepAliveを使用すると、一度だけ実行され、次回以降のアクセスでは、ルーターはすでにレンダリングされたページのDOM要素を再利用します。

ページの状態を本当に維持する必要がある場合にも役立ちます. keepAliveを有効にすると、次回ページが読み込まれたときに、すべてのDOMの変更とフォーム要素の状態が保持されます.

ただし、注意すべき点があります。

コンポーネントとページのライフサイクルの落とし穴を避けるために、次のページイベントに依存することをお勧めします.

keepAliveルートを作成するには、パラメータに keepAlive: true を渡すだけです。

import SomPageComponent from './some-page.js';

var routes = [
  /* Usual route */
  {
    path: '/',
    url: './pages/home.html',
  },
  /* Alive route. Will be loaded from file first time, and then will reuse rendered DOM element */
  {
    path: '/map/',
    url: './pages/map.html',
    keepAlive: true,
  },

  /* Alive route. Will be created as component, and then will reuse rendered DOM element */
  {
    path: '/some-page/',
    component: SomPageComponent,
    keepAlive: true,
  },
];