本文へスキップ
バージョン:v8

Vue ナビゲーション

このガイドでは、Ionic と Vue を使用して構築されたアプリでのルーティングの仕組みについて説明します。

IonRouterOutlet コンポーネントは、人気のある Vue Router ライブラリを内部で使用しています。Ionic と Vue Router を使用すると、豊富なページ遷移を備えた複数ページアプリを作成できます。

Vue Router を使用したルーティングに関する知識は、Ionic Vue でもそのまま適用できます。Ionic Vue アプリの基本と、ルーティングの仕組みを見てみましょう。

簡単なメモ

このガイドを読んでいると、これらの概念のほとんどが、Ionic Framework を使用しない Vue Router にある概念と非常に似ていることに気付くかもしれません。それは正しいです!Ionic Vue は Vue Router の優れた部分を活用して、Ionic Framework を使用したアプリ構築への移行を可能な限りシームレスにします。そのため、独自のルーティングソリューションを作成しようとするのではなく、できるだけ Vue Router の機能に依存することをお勧めします。

シンプルなルート

ここでは、"/home" URL に単一のルートを定義するサンプルルーティング構成を示します。"/home" にアクセスすると、ルートは HomePage コンポーネントをレンダリングします。

router/index.ts

import { createRouter, createWebHistory } from '@ionic/vue-router';
import { RouteRecordRaw } from 'vue-router';
import HomePage from '@/views/Home.vue';

const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: HomePage,
},
];

const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});

export default router;

アプリの最初の読み込み時に、ここで設定されているように、アプリは HomePage コンポーネントをレンダリングします。

リダイレクトの処理

最初の読み込み時に別のパスに移動したい場合はどうでしょうか?そのためには、ルーターリダイレクトを使用できます。リダイレクトは通常のルートオブジェクトと同じように機能しますが、いくつかの異なるキーが含まれています。

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/home',
},
{
path: '/home',
name: 'Home',
component: HomePage,
},
];

リダイレクトでは、アプリのインデックスパスを探します。そして、それをロードすると、home ルートにリダイレクトされます。

これは素晴らしいのですが、実際にルートに移動するにはどうすればよいのでしょうか?そのためには、router-link プロパティを使用できます。新しいルーティング設定を作成してみましょう。

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/home',
},
{
path: '/home',
name: 'Home',
component: HomePage,
},
{
path: '/detail',
name: 'Detail',
component: DetailPage,
},
];

home ルートから開始し、detail ルートに移動するボタンを追加したいとします。detail ルートに移動するには、次の HTML を使用できます。

<ion-button router-link="/detail">Go to detail</ion-button>

ルーターAPIを使用して、アプリでプログラムによってナビゲーションすることもできます。

<template>
<ion-page>
<ion-content>
<ion-button @click="() => router.push('/detail')">Go to detail</ion-button>
</ion-content>
</ion-page>
</template>

<script lang="ts">
import { IonButton, IonContent, IonPage } from '@ionic/vue';
import { defineComponent } from 'vue';
import { useRouter } from 'vue-router';

export default defineComponent({
name: 'HomePage',
components: {
IonButton,
IonContent,
IonPage,
},
setup() {
const router = useRouter();
return { router };
},
});
</script>

どちらのオプションも、異なるユースケースに適合する同じナビゲーションメカニズムを提供します。

router-link 属性は、任意の Ionic Vue コンポーネントに設定でき、コンポーネントがクリックされると、指定されたルートにルーターが移動します。router-link 属性は、Vue Router の router.push と同様に、文字列値と名前付きルートの両方を許可します。より詳細な制御のために、router-direction 属性と router-animation 属性も設定できます。

router-direction 属性は、forwardback、または none の値を受け入れ、ページ遷移の方向を制御するために使用されます。

router-animation 属性は AnimationBuilder 関数を受け入れ、コンポーネントをクリックしたときにのみ使用されるカスタムページ遷移を提供するために使用されます。AnimationBuilder 型は、Ionic Animation インスタンスを返す関数です。Ionic Vue でアニメーションを使用する方法の詳細については、アニメーションドキュメントを参照してください。

<ion-button router-link="/page2" router-direction="back" :router-animation="myAnimation">Click Me</ion-button>

router-link を使用することの欠点の1つは、ナビゲーション前にカスタムコードを実行できないことです。これにより、ナビゲーション前にネットワークリクエストを発火するなどのタスクが困難になります。Vue Router を直接使用できますが、ページ遷移を制御する機能が失われます。ここで、useIonRouter ユーティリティが役立ちます。

useIonRouter ユーティリティは、ページ遷移を完全に制御しながら、プログラムによるナビゲーションのためのメソッドを提供する関数です。これにより、ナビゲーション前にカスタムコードを簡単に実行できます。

この最初の例では、カスタムページ遷移を使用して、新しいページをスタックにプッシュできます。

import { defineComponent } from 'vue';
import { useIonRouter } from '@ionic/vue';
import { customAnimation } from '@/animations/customAnimation';

export default defineComponent({
...,
setup() {
const ionRouter = useIonRouter();

ionRouter.push('/page2', customAnimation);
}
});

useIonRouter は、一般的なナビゲーションアクションを簡単に使用できるように、便利な pushreplaceback、および forward メソッドを提供します。また、より複雑なナビゲーションシナリオで使用できる navigate メソッドも提供します。

import { defineComponent } from 'vue';
import { useIonRouter } from '@ionic/vue';
import { customAnimation } from '@/animations/customAnimation';

export default defineComponent({
...,
setup() {
const ionRouter = useIonRouter();

ionRouter.navigate('/page2', 'forward', 'replace', customAnimation);
}
});

上記の例では、forward方向を使用するカスタムアニメーションを使用して、アプリが/page2 に移動します。さらに、replace 値により、ナビゲーション時にアプリが現在の履歴エントリを置き換えます。

useIonRouter は Vue の inject() 関数を使用し、setup() 関数の内部でのみ使用する必要があります。

詳細と型情報については、useIonRouter ドキュメントを参照してください。

Vue Router には、開発者がアプリケーション履歴を前後に移動できるようにする router.go メソッドがあります。例を見てみましょう。

次のアプリケーション履歴があるとします。

/pageA --> /pageB --> /pageC

/pageCrouter.go(-2) を呼び出すと、/pageA に戻されます。次に router.go(2) を呼び出すと、/pageC に移動します。

router.go() の重要な特性は、アプリケーション履歴が線形であることを期待していることです。つまり、router.go() は非線形ルーティングを使用するアプリケーションでは使用しないでください。詳細については、線形ルーティングと非線形ルーティングを参照してください。

遅延読み込みルート

現在のルートの設定方法では、アプリの読み込み時に同じ初期チャンクに含まれるため、常に理想的とは限りません。代わりに、必要なときにコンポーネントが読み込まれるようにルートを設定できます。

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/home',
},
{
path: '/home',
name: 'Home',
component: HomePage,
},
{
path: '/detail',
name: 'Detail',
component: () => import('@/views/DetailPage.vue'),
},
];

ここでは、以前と同じ設定をしていますが、今回は DetailPage がインポート呼び出しに置き換えられています。これにより、DetailPage コンポーネントは、アプリケーションのロード時に要求されるチャンクの一部ではなくなります。

線形ルーティングと非線形ルーティング

線形ルーティング

ルーティングを使用するウェブアプリを構築したことがある場合は、以前に線形ルーティングを使用している可能性があります。線形ルーティングとは、ページをプッシュおよびポップすることで、アプリケーション履歴を前後に移動できることを意味します。

以下は、モバイルアプリでの線形ルーティングの例です。

この例でのアプリケーション履歴は、次のパスになります。

アクセシビリティ --> VoiceOver --> 音声

戻るボタンを押すと、同じルーティングパスを逆順にたどります。リニアルーティングは、シンプルで予測可能なルーティング動作を可能にするため役立ちます。また、router.go()などのVue Router APIを使用できることも意味します。

リニアルーティングの欠点は、タブビューなどの複雑なユーザーエクスペリエンスを許容しないことです。これが、非リニアルーティングが登場するところです。

非リニアルーティング

非リニアルーティングは、Ionicを使用してモバイルアプリを構築することを学んでいる多くのWeb開発者にとって、新しい概念かもしれません。

非リニアルーティングとは、ユーザーが戻るべきビューが、画面に表示されていた前のビューとは必ずしも一致しないことを意味します。

以下は、非リニアルーティングの例です。

上記の例では、Originalsタブから開始します。カードをタップすると、Originalsタブ内のTed Lassoビューに移動します。

ここからSearchタブに切り替えます。次に、もう一度Originalsタブをタップすると、Ted Lassoビューに戻ります。この時点で、非リニアルーティングを使用し始めました。

なぜこれが非リニアルーティングなのでしょうか?直前のビューはSearchビューでした。しかし、Ted Lassoビューで戻るボタンを押すと、ルートOriginalsビューに戻ります。これは、モバイルアプリの各タブが独自のスタックとして扱われるためです。タブの操作セクションで、これについて詳しく説明します。

Ted Lassoビューから戻るボタンをタップすると単にrouter.go(-1)が呼び出された場合、正しい動作ではなくSearchビューに戻ってしまいます。

非リニアルーティングにより、リニアルーティングでは処理できない高度なユーザーフローが可能になります。ただし、router.go()などの特定のリニアルーティングAPIはこの非リニア環境では使用できません。つまり、タブまたはネストされたアウトレットを使用する場合は、router.go()を使用しないでください。

どちらを選択すべきか?

非リニアルーティングを追加する必要があるまで、アプリケーションをできるだけシンプルに保つことをお勧めします。非リニアルーティングは非常に強力ですが、モバイルアプリケーションにかなりの複雑さを追加します。

非リニアルーティングの最も一般的な2つの用途は、タブとネストされたion-router-outletです。アプリケーションがタブまたはネストされたルーターアウトレットのユースケースを満たしている場合にのみ、非リニアルーティングを使用することをお勧めします。

タブの詳細については、タブの操作を参照してください。

ネストされたルーターアウトレットの詳細については、ネストされたルートを参照してください。

共有URLとネストされたルート

ルーティングを設定する際の一般的な混乱点は、共有URLとネストされたルートのどちらを選択するかです。このガイドのこの部分では、両方について説明し、どちらを使用するかを決定するのに役立ちます。

共有URL

共有URLとは、URLの一部を共有するルート構成のことです。以下は、共有URL構成の例です。

const routes: Array<RouteRecordRaw> = [
{
path: '/dashboard',
component: DashboardMainPage,
},
{
path: '/dashboard/stats',
component: DashboardStatsPage,
},
];

上記のルートは、URLのdashboard部分を再利用するため、「共有」と見なされます。

ネストされたルート

ネストされたルートとは、ルートが他のルートの子としてリストされているルート構成のことです。以下は、ネストされたルート構成の例です。

const routes: Array<RouteRecordRaw> = [
{
path: '/dashboard/:id',
component: DashboardRouterOutlet,
children: [
{
path: '',
component: DashboardMainPage,
},
{
path: 'stats',
component: DashboardStatsPage,
},
],
},
];

上記のルートは、親ルートのchildren配列内にあるため、ネストされています。親ルートはDashboardRouterOutletコンポーネントをレンダリングすることに注意してください。ルートをネストする場合は、別のion-router-outletインスタンスをレンダリングする必要があります。

どちらを選択すべきか?

共有URLは、ページAからページBへの遷移を、URL内の2つのページ間の関係を維持しながら行いたい場合に最適です。前の例では、/dashboardページのボタンによって/dashboard/statsページに遷移できます。2つのページ間の関係は、a) ページ遷移とb) URLの両方によって維持されます。

ネストされたルートは、アウトレットAにコンテンツをレンダリングすると同時に、ネストされたアウトレットB内にサブコンテンツをレンダリングする場合に使用します。最も一般的なユースケースはタブです。タブIonicスターターアプリケーションをロードすると、最初のion-router-outletion-tab-barion-tabsコンポーネントがレンダリングされているのがわかります。ion-tabsコンポーネントは、各タブのコンテンツをレンダリングする別のion-router-outletをレンダリングします。

モバイルアプリケーションでネストされたルートが意味を持つユースケースは非常に少ないです。不明な場合は、共有URLルート構成を使用してください。タブ以外のコンテキストでネストされたルーティングを使用することは、アプリのナビゲーションを混乱させる可能性があるため、強くお勧めしません。

タブの操作

タブを操作する場合、Ionic Vueはどのビューがどのタブに属するかを知る必要があります。ここではIonTabsコンポーネントが役立ちますが、そのルーティング設定を見てみましょう。

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/tabs/tab1',
},
{
path: '/tabs/',
component: Tabs,
children: [
{
path: '',
redirect: 'tab1',
},
{
path: 'tab1',
component: () => import('@/views/Tab1.vue'),
},
{
path: 'tab2',
component: () => import('@/views/Tab2.vue'),
},
{
path: 'tab3',
component: () => import('@/views/Tab3.vue'),
},
],
},
];

ここでは、tabsパスはTabsコンポーネントをロードします。各タブをchildren配列内のルートオブジェクトとして提供します。この例では、パスをtabsと呼びますが、これはカスタマイズできます。

まず、Tabsコンポーネントを見てみましょう。

<template>
<ion-page>
<ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="bottom">
<ion-tab-button tab="tab1" href="/tabs/tab1">
<ion-icon :icon="triangle" />
<ion-label>Tab 1</ion-label>
</ion-tab-button>

<ion-tab-button tab="tab2" href="/tabs/tab2">
<ion-icon :icon="ellipse" />
<ion-label>Tab 2</ion-label>
</ion-tab-button>

<ion-tab-button tab="tab3" href="/tabs/tab3">
<ion-icon :icon="square" />
<ion-label>Tab 3</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
</ion-page>
</template>

<script lang="ts">
import { IonTabBar, IonTabButton, IonTabs, IonLabel, IonIcon, IonPage, IonRouterOutlet } from '@ionic/vue';
import { ellipse, square, triangle } from 'ionicons/icons';

export default {
name: 'Tabs',
components: {
IonLabel,
IonTabs,
IonTabBar,
IonTabButton,
IonIcon,
IonPage,
IonRouterOutlet,
},
setup() {
return {
ellipse,
square,
triangle,
};
},
};
</script>

以前にIonic Frameworkを使用していたことがある場合は、これは馴染みのあるものだと思います。ion-tabsコンポーネントを作成し、ion-tab-barを提供します。ion-tab-barは、ルーター構成内の対応するタブに関連付けられたtabプロパティを持つion-tab-buttonコンポーネントを提供します。また、ion-tabsにさまざまなタブビューをレンダリングするためのアウトレットを提供するために、ion-router-outletも提供します。

Ionicでのタブの動作

Ionicの各タブは、個々のナビゲーションスタックとして扱われます。つまり、アプリケーションに3つのタブがある場合、各タブには独自のナビゲーションスタックがあります。各スタック内では、前方向(ビューのプッシュ)と後方向(ビューのポップ)に移動できます。

この動作は、他のWebベースのUIライブラリにあるほとんどのタブの実装とは異なるため、注意が必要です。他のライブラリは通常、タブを単一の履歴スタックとして管理します。

Ionicは開発者がモバイルアプリを構築するのを支援することに重点を置いているため、Ionicのタブはネイティブモバイルタブにできるだけ近づけるように設計されています。その結果、Ionicのタブには、他のUIライブラリで見たタブの実装とは異なる動作がある場合があります。これらの違いの詳細については、以下をお読みください。

タブ内の子ルート

タブに追加のルートを追加する場合は、親タブをパスプレフィックスとして持つ兄弟ルートとして記述する必要があります。以下の例では、/tabs/tab1/viewルートを/tabs/tab1ルートの兄弟として定義しています。この新しいルートにはtab1プレフィックスがあるため、Tabsコンポーネント内でレンダリングされ、ion-tab-barではタブ1が引き続き選択されます。

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/tabs/tab1',
},
{
path: '/tabs/',
component: Tabs,
children: [
{
path: '',
redirect: 'tab1',
},
{
path: 'tab1',
component: () => import('@/views/Tab1.vue'),
},
{
path: 'tab1/view',
component: () => import('@/views/Tab1View.vue'),
},
{
path: 'tab2',
component: () => import('@/views/Tab2.vue'),
},
{
path: 'tab3',
component: () => import('@/views/Tab3.vue'),
},
],
},
];

タブ間の切り替え

各タブは独自のナビゲーションスタックであるため、これらのナビゲーションスタックは決して相互作用すべきではないことに注意することが重要です。つまり、タブ1にユーザーをタブ2にルーティングするボタンがあってはなりません。言い換えれば、タブは、ユーザーがタブバーのタブボタンをタップすることによってのみ変更する必要があります。

実際におけるこの良い例は、iOS App StoreとGoogle Play Storeのモバイルアプリケーションです。これらのアプリは両方ともタブ付きインターフェースを提供していますが、どちらのアプリもユーザーをタブ間でルーティングすることはありません。たとえば、iOS App Storeアプリの「ゲーム」タブは、ユーザーを「検索」タブに誘導することはなく、その逆も同様です。

タブでよくある間違いをいくつか見てみましょう。

複数のタブが参照する設定タブ

設定ビューを独自のタブとして作成するのが一般的な方法です。開発者が複数のネストされた設定メニューを表示する必要がある場合に最適です。ただし、他のタブは設定タブにルーティングしようとするべきではありません。前述のように、設定タブをアクティブ化できるのは、ユーザーが適切なタブボタンをタップした場合のみです。

タブで設定タブを参照する必要がある場合は、ion-modalを使用して設定ビューをモーダルにすることをお勧めします。これはiOS App Storeアプリで見られる方法です。このアプローチでは、どのタブでもモーダルを表示でき、各タブが独自のスタックであるモバイルタブのパターンを破らずに済みます。

以下の例は、iOS App Storeアプリが複数のタブから「アカウント」ビューを表示する方法を示しています。「アカウント」ビューをモーダルで表示することで、アプリはモバイルタブのベストプラクティスに従って、複数のタブで同じビューを表示できます。

タブ間でのビューの再利用

別の一般的な方法は、同じビューを複数のタブに表示することです。開発者は、ビューを単一のタブに含め、他のタブをそのタブにルーティングすることで、これを行うことがよくあります。前述のように、これはモバイルタブのパターンを破るため、避ける必要があります。

代わりに、各タブに同じコンポーネントを参照するルートを作成することをお勧めします。これは、Spotifyなどの一般的なアプリで行われている方法です。たとえば、「ホーム」、「検索」、「ライブラリ」タブからアルバムやポッドキャストにアクセスできます。アルバムやポッドキャストにアクセスする場合、ユーザーはそのタブ内に留まります。アプリは、タブごとにルートを作成し、コードベースで共通のコンポーネントを共有することでこれを実現しています。

以下の例は、Spotifyアプリが同じアルバムコンポーネントを再利用して複数のタブにコンテンツを表示する方法を示しています。各スクリーンショットで同じアルバムが表示されていますが、タブが異なります。

ホームタブ検索タブ

コンポーネント

IonRouterOutlet

IonRouterOutletコンポーネントは、ビューをレンダリングするためのコンテナーを提供します。これは、他のVueアプリケーションにあるRouterViewコンポーネントに似ていますが、IonRouterOutletは同じアウトレットで複数のページをDOMにレンダリングできます。IonRouterOutletでコンポーネントがレンダリングされると、これはIonic Frameworkの「ページ」と見なされます。ルーターアウトレットコンテナーは、ページ間の遷移アニメーションを制御し、ページの作成と破棄を制御します。これにより、ビュー間を切り替える際のビューの状態を維持できます。

テンプレートで設定する際に、IonRouterOutlet内に何も提供する必要はありません。IonRouterOutletは子コンポーネントにネストできますが、通常はアプリのナビゲーションを混乱させるため、お勧めしません。詳細については、共有URLとネストされたルートを参照してください。

IonPage

IonPageコンポーネントは、Ionic Vueアプリ内の各ビューをラップし、ページ遷移とスタックナビゲーションが正しく機能するようにします。ルーターを使用してナビゲートされる各ビューには、IonPageコンポーネントを含める必要があります。

<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Home</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">Hello World</ion-content>
</ion-page>
</template>

<script lang="ts">
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue';
import { defineComponent } from 'vue';

export default defineComponent({
components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
},
});
</script>

IonModalまたはIonPopoverを介して表示されるコンポーネントには、通常、ラッパー要素が必要ない限り、IonPageコンポーネントは必要ありません。その場合、コンポーネントのサイズが正しく計算されるように、IonPageを使用することをお勧めします。

関数

useIonRouter

useIonRouter(): UseIonRouterResult

Ionicルーターインスタンスを返します。これには、ページの遷移をナビゲート、カスタマイズするためのAPIメソッド、およびネイティブ機能のルーティングコンテキストが含まれています。この関数は、VueのuseRouter関数と組み合わせて使用できます。

使用例については、ユーティリティ関数を参照してください。

URLパラメータ

元のルーティング例を拡張して、URLパラメータの使用方法を示します。

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/home',
},
{
path: '/home',
name: 'Home',
component: HomePage,
},
{
path: '/detail/:id',
name: 'Detail',
component: DetailPage,
},
];

detailパス文字列の最後に:idを追加しました。URLパラメータは、ルートパスの動的な部分です。ユーザーが/details/1などのURLに移動すると、「1」は「id」という名前のパラメータに保存され、ルートがレンダリングされるときにコンポーネントでアクセスできます。

コンポーネントでの使用方法を見てみましょう。

<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Details</ion-title>
</ion-toolbar>
</ion-header>

<ion-content> Detail ID: {{ id }} </ion-content>
</ion-page>
</template>

<script lang="ts">
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue';
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';

export default defineComponent({
name: 'Detail',
components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
},
setup() {
const route = useRoute();
const { id } = route.params;
return { id };
},
});
</script>

route変数には、現在のルートのインスタンスが含まれています。また、渡されたパラメータも含まれています。ここからidパラメータを取得して、画面に表示できます。

ルーター履歴

Vue Routerには、構成可能な履歴モードが付属しています。さまざまなオプションと、それぞれを使用する理由を見てみましょう。

  • createWebHistory: このオプションはHTML5履歴を作成します。History APIを活用して、ページを再読み込みせずにURLナビゲーションを実現します。シングルページアプリケーションで最も一般的な履歴モードです。迷ったら、createWebHistoryを使用してください。

  • createWebHashHistory: このオプションは、URLにハッシュ(#)を追加します。これは、ホストがないWebアプリケーション、またはサーバールートを完全に制御できない場合に役立ちます。検索エンジンはハッシュフラグメントを無視することがあるため、アプリケーションにとってSEOが重要な場合は、代わりにcreateWebHistoryを使用する必要があります。

  • createMemoryHistory: このオプションは、メモリベースの履歴を作成します。これは主に、サーバーサイドレンダリング(SSR)の処理に使用されます。

詳細情報

Vue Routerを使用したVueでのルーティングの詳細については、http://router.vuejs.org/のドキュメントをご覧ください。