[Vue.js3] 8. Vue Router

  1. 8.1 シングルページアプリケーション
    1. 8.1.1 ルーティングとは
      1. サンプルプロジェクト router-basic の画面の対応関係表
    2. 8.1.2 サーバーサイド Web アプリケーションでの処理の流れ
    3. 8.1.3 シングルページアプリケーションとは
  2. 8.2 Vue Router の基本
    1. 8.2.1 Vue Router のプロジェクト
    2. 8.2.2 画面用コンポーネントを入れる views フォルダ
      1. サンプルプロジェクト router-basic の画面用コンポーネント
    3. 8.2.3 ルーティング表示領域の設定
      1. App.vue
    4. 8.2.4 ルーティング設定の基本構文
      1. AppTop.vue
      2. index.ts
        1. あらかじめ存在するコードを利用
        2. TypeScriptの恩恵を受けるよう改変
    5. 8.2.5 コンポーネントの動的インポート設定
    6. 8.2.6 ルーティングリンクの設定
  3. 8.3 ルートパラメータ
    1. 8.3.1 ルートパラメータの設定
    2. 8.3.2 ルートパラメータの埋め込み
    3. 8.3.3 ルートパラメータを Props として取得
  4. 8.4 スクリプトブロックでのルーティング制御
    1. 8.4.1 会員情報追加画面の作成
    2. 8.4.2 ルーティングを制御する Router オブジェクト
      1. 入力データを会員リストデータに追加する
      2. リスト画面に遷移する
    3. 8.4.3 Router オブジェクトの他のメソッド
  5. 8.5 Vue router のその他の機能
    1. 8.5.1 スクリプトブロックでのルーティング情報の取得
    2. 8.5.2 さまざまなルートパラメータ
      1. 複数パラメータ
      2. 省略可能パラメータ
      3. 可変長パラメータ
      4. 値のチェック
      5. 404 Not Found
    3. 8.5.3 ネストされたルーティング
    4. 8.5.4 マルチビュー
      1. App.vue
      2. index.ts
      3. コンポーネント
      4. 実行結果
    5. 8.5.5 ナビゲーションガード
      1. Routerオブジェクトのガードメソッド
      2. ナビゲーションガードの呼び出し順番
      3. グローバルガードでの定義方法
      4. ルーティングごとのガードの設定方法
      5. コンポーネントごとのガードの設定方法
      6. ナビゲーションガード設定の引数
    6. 8.5.6 リダイレクト

8.1 シングルページアプリケーション

公式ライブラリ Vue Router を利用することでシングルページアプリケーションを簡単に作成できるようになる。

8.1.1 ルーティングとは

画面遷移を伴うアプリケーションにおいて表示させる画面とパスを結び付け、特定のパスに対応して画面表示処理を行うことをルーティングという。
Vue Router は Vue と組み合わせてルーティング処理を行うライブラリである。

サンプルプロジェクト router-basic の画面の対応関係表

番号画面名パス概要
1TOP 画面/最初の画面
[会員管理はこちら]リンククリックで2.会員リスト画面が表示される
2会員リスト画面/member/memberList会員情報はリンクとなっており、クリックすると3.会員詳細情報画面が表示される
[新規登録はことらから]リンクをクリックすると4.会員情報追加画面が表示される
3会員詳細情報画面/member/detail/334456パス末尾「334456」は会員によって変化する
4会員情報追加画面/member/add必要情報を入力して[登録]ボタンをクリックすると会員情報が保存され2.会員リスト画面に戻りリストに追加される

8.1.2 サーバーサイド Web アプリケーションでの処理の流れ

青:ブラウザ
緑:Webサーバー

  1. ブラウザから TOP 画面を表示させるパスであるルート(/)にアクセス
  2. Web サーバー側で処理が行われ TOP 画面表示に必要な HTML データが生成される
  3. それを受け取ったブラウザがデータをレンダリングすることで TOP 画面が表示される
  4. TOP 画面の[会員管理はこちら]のリンクをクリック
  5. 会員リスト画面を表示させるパスである /member/memberList にアクセスする
  6. Web サーバー側で処理が行われ会員リスト画面表示に必要な HTML データが生成される
  7. それを受け取ったブラウザがデータをレンダリングすることで会員リスト画面が表示される
  8. 以降繰り返し

このようにサーバーサイド Web アプリケーションではリンクやボタンがクリックされるたびにサーバーにアクセスが発生し、サーバーではそれを処理し、表示に必要な HTML データを原則全てサーバー側で生成する。
サーバー側で処理が行われるためサーバーサイド Web アプリケーションと呼ばれる。

8.1.3 シングルページアプリケーションとは

Vue プロジェクトで、初回にサーバーから読み込む HTML データは index.html のみである。
その index.html に付属して読み込まれる JavaScript コードがブラウザで実行されることでさまざまな画面表示が行われる。

これは Vue Router を利用したサンプルプロジェクト router-basic でも同様で、index.html が表示された後 JavaScript によってルート(/)パスに対応する TOP 画面がレンダリングされる。
この後 [会員管理はこちら] のリングをクリックすると /member/memberList パスに対応する会員リスト画面が JavaScript によってレンダリングされる。

このようにサーバーから読み込む HTML データ(ページ)がひとつしかなく(シングル)、後の画面表示処理をブラウザ側の JavaScript で行うアプリケーションをシングルページアプリケーション(Single Page Applicaten の頭文字をとって SPA)という。

レンダリング途中のサーバーアクセス

各画面をレンダリングする際にサーバー上のデータが必要な場合も JavaScript コードによってサーバーへアクセスし、取得したデータをもとにレンダリングを行う。

8.2 Vue Router の基本

8.2.1 Vue Router のプロジェクト

Vue Router を利用するプロジェクトでは、Vue プロジェクト作成時の質問「Add Vue Router for Single Page Application development?」「Yes」を選択する。

2025.4.21 最近の画面は下記

プロジェクトの作成が終了すると、srcフォルダ内に router フォルダと views フォルダが追加される。

router フォルダ内には index.ts ファイルがあらかじめ作られており、このファイルがルーティングの設定を行う。

index.ts
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView,
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (About.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import('../views/AboutView.vue'),
    },
  ],
})

export default router
main.ts 内コードの違い

プロジェクト生成時に生成される main.ts に Vue Router が動作するよう記述が追加される。

import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(router)

app.mount('#app')

8.2.2 画面用コンポーネントを入れる views フォルダ

多くの Web アプリケーションは画面遷移によって変化する部分と変化しない部分に分かれる

Vue Router サンプル(この部分は画面遷移で変化しない)

(この枠線の中だけが画面遷移で変化する)
TOP

TOP

会員管理はこちら

シングルページアプリケーションでは、このように変化する部分んだけを JavaScript によってレンダリングしていく。
この変化する部分がルーティング表示領域であり、その表示領域にそれぞれのコンポーネントをあてはめることで画面が切り替わっていく仕組みとなる。

サンプルプロジェクト router-basic の画面用コンポーネント

route-basicプロジェクトでは画面遷移によって変化しない枠線の外側の部分を App.vue に記述し、画面遷移によって変化する枠線の内側の部分をルーティング表示領域としてそこに表示するコンポーネントとして下記表のコンポーネントファイルを用意する。

番号画面名コンポーネントファイル名
1TOP 画面AppTop.vue
2会員リスト画面member/MemberList.vue
3会員詳細情報画面member/MemberDetail.vue
4会員情報追加画面member/MemberAdd.vue

これらのルーティング表示領域にレンダリングさせるコンポーネント(画面用コンポーネント)を入れておくフォルダが views フォルダである。

views フォルダ内にサブフォルダを作成してユースケースごとに画面用コンポーネントを分けておくこともできる。
ここでは表のように2~4は会員管理関連の画面なので member フォルダ内にまとめて入れておくことにする。

views フォルダと components フォルダ

これまで App.vue 以外のコンポーネントファイルは components フォルダに格納していた。
Vue Router を利用したプロジェクトでも components フォルダは利用できる。
views フォルダに入れておくのはあくまでルーティング表示領域にレンダリングするためのコンポーネントで、そのため views フォルダ内のコンポーネント数は原則画面数と一致する。
一方それらの画面表示用コンポーネントからさらに子コンポーネントとして利用するコンポーネントなどは components フォルダに入れる。

8.2.3 ルーティング表示領域の設定

App.vue

src/App.vue
<script setup lang="ts">
import { reactive, provide } from 'vue'
import { RouterView } from 'vue-router' // 1.
import type { Member } from '@/interfaces' // 2. @はsrcディレクトリを指す

// 3. コンポーネントを複数またいでデータを共有する必要があるので、Provide と inject を利用する。
// 一番の起点となる App.vue で会員リストデータを Provicde しておく。
const memberList = new Map<number, Member>()
memberList.set(33456, {
  id: 33456,
  name: '田中太郎',
  email: 'bou@example.com',
  points: 35,
  note: '初回入会特典あり。',
})
memberList.set(47783, { id: 47783, name: '鈴木二郎', email: 'mue@example.com', points: 53 })
provide('memberList', reactive(memberList))
</script>

<template>
  <!-- 4. ヘッダ -->
  <header>
    <h1>Vue Routerサンプル</h1>
  </header>

  <!-- 5. ルーティング表示領域を囲むタグ <main> -->
  <main>
    <!-- 6. ルーティング表示領域 (使用前にvue-routerからRouterViewをインポートしておく(1.))-->
    <RouterView />
  </main>
</template>

<style>
/* 7. main タグに枠線を表示する設定。 */
main {
  border: blue 1px solid;
  padding: 10px;
}

/* 8. 以下はパンくずリストを表示するための定型スタイル */
#breadcrumbs ul li {
  display: inline;
  list-style-type: none;
}
#breadcrumbs {
  margin-left: 0px;
}
#breadcrumbs ul {
  padding-left: 0px;
}
#breadcrumbs ul .current {
  color: red;
}
#breadcrumbs ul li:before {
  content: ' > ';
}
#breadcrumbs ul li:first-child:before {
  content: none;
}
</style>

8.2.4 ルーティング設定の基本構文

AppTop.vue

Top画面を表示する AppTop.vue を src/views フォルダ直下に作成

src/views/AppTop.vue
<template>
  <h1>TOP</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>TOP</li>
    </ul>
  </nav>
  <section>
    <p>会員管理はこちら</p>
  </section>
</template>

index.ts

URLがルート (/) パスの際に AppTop.vue をルーティング表示領域に表示するようにルーティングを設定する。
ルーティング設定は router フォルダ内の index.ts ファイルへ記述する。

あらかじめ存在するコードを利用
TypeScript
import { createRouter, createWebHistory } from 'vue-router'


import AppTop from '@/views/AppTop.vue'










const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'AppTop',
      component: AppTop,
    },
  ],
})

export default router
TypeScriptの恩恵を受けるよう改変
TypeScript
import { createRouter, createWebHistory } from 'vue-router'
// routes 配列の型定義
import type { RouteRecordRaw } from 'vue-router'
import AppTop from '@/views/AppTop.vue'

// routesプロパティの値を分離した
const routeSettings: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'AppTop',
    component: AppTop,
  },
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: routeSettings,
})







export default router
const routeSettings: RouteRecordRaw[] = [
  {
    path: "パス文字列",
    name: "ルーティング名",
    component: 表示させる画面用コンポーネント
  },
   ⋮
]

Vue Router でルーティング設定を行う場合、一番大切なのは createRouter() 関数を実行し、その結果をexport すること。

createRouter() の引数にはルーティングに関する設定をオブジェクトとして渡す。
routes: ルーティング情報配列を指定するプロパティ
history: historyの設定を表す

8.2.5 コンポーネントの動的インポート設定

Topコンポーネント内の p タグの「会員管理はこちら」にリンクを設定。
まずリンク先のルーティング情報を登録する。

import { createRouter, createWebHistory } from 'vue-router'
// routes 配列の型定義
import type { RouteRecordRaw } from 'vue-router'
import AppTop from '@/views/AppTop.vue'

const routeSettings: RouteRecordRaw[] = [
  { // AppTop ルーティング設定
    path: '/',
    name: 'AppTop',
    component: AppTop,
  },
  { // MemberList ルーティング設定
    path: "/member/memberList",    // 1.
    name: "MemberList",            // 2.
    component: () => {             // 3.
      return import("@/views/member/MemberList.vue");
    }
  }
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: routeSettings,
})

export default router
  1. path プロパティ
    パスを表す /member/memberList
  2. name プロパティ
    コンポーネント名と同じ MemberList としている。
    AppTop.vueのリンク設定「<RouterLink v-bind:to=”{name: ‘MemberList’}”>」で利用する。
  3. component プロパティ
    ルートパスとして表示するAppTopコンポーネントのAppTopルーティング設定ではあらかじめインポートしたAppTopコンポーネントを指定していて、この方法の場合 Vue プロジェクトのビルド時に該当コンポーネントがインポートされる。

    表示が必要がどうかわからない画面の場合、必要になってから使用する画面用コンポーネントをインポートする仕組みがあり、これを動的インポート(dynamic imports)という。
    MemberListはこの仕組みを使っている。
    ルートパス画面コンポーネント以外は原則この動的インポートを利用する。
component: () => {
  return import("インポートする画面用コンポーネントファイルパス");

8.2.6 ルーティングリンクの設定

会員リスト画面用のコンポーネントファイル MembreList.vue を作成。
まずはコンポーネントを表示することを目的とするので最小限のコードのみ記述。

views/member/MemberList.vue
<template>
  <h1>会員管理</h1>
  <section>
    <h2>会員リスト</h2>
  </section>
</template>

AppTop コンポーネントにリンクを設定。

<script setup lang="ts">
import { RouterLink } from 'vue-router'
</script>

<template>
  <h1>TOP</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>TOP</li>
    </ul>
  </nav>
  <section>
    <p>
      <RouterLink v-bind:to="{ name: 'MemberList' }"> 会員管理はこちら </RouterLink>
    </p>
  </section>
</template>

このリンクはVue Router によって制御されたリンクなので通常の a タグは使えない。
専用のタグ RouterLink を使う

<RouterLink v-bind:to="{name: 'ルーティング名'}">
  ︙
</RouterLink>

Vue Routerによって制御されたリンクを設定するには、RouterLinkをインポートしたうえでRouterLink タグを記述し v-bond:to ディレクティブを利用する。
ルーティング名とは、router/index.ts にルーティング設定として記述した各ルーティング情報オブジェクトの name プロパティの値を指す。
ルーティング名は属性値内に記述することになるのでシングルクォーテーションで囲む

8.3 ルートパラメータ

8.3.1 ルートパラメータの設定

会員リスト画面(MemberList.vue)にパンくずリストと会員リストの表示部分を追記する。
パンくずリストにはTop画面に戻るリンクがあり、RouteLinkタグ構文を利用する。
会員詳細情報画面では会員情報を特定する会員idを受け取る必要があるので、会員リストのそれぞれのリンクのパスの末尾に画面を表示させるためのデータをパラメータとして埋め込むようにする。このパラメータを埋め込むことをルートパラメータ(Prams)という。

views/member/MemberList.vue
<script setup lang="ts">
import { inject } from 'vue'
import { RouterLink } from 'vue-router'
import type { Member } from '@/interfaces'

const memberList = inject('memberList') as Map<number, Member>
</script>

<template>
  <h1>会員管理</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>
        <!-- Top画面に戻るリンク。ReouteLinkタグ構文を利用 -->
        <RouterLink v-bind:to="{ name: 'AppTop' }"> TOP </RouterLink>
      </li>
    </ul>
  </nav>
  <section>
    <h2>会員リスト</h2>
    <section>
      <ul>
        <li v-for="[id, member] in memberList" v-bind:key="id">
          <!-- ルートパラメータの埋め込み。params:{ルートパラメータ名: 値,…} -->
          <RouterLink v-bind:to="{ name: 'MemberDetail', params: { id: id } }">
            IDが{{ id }}の{{ member.name }}さん
          </RouterLink>
        </li>
      </ul>
    </section>
  </section>
</template>

会員詳細画面(MemberDetail.vue)のコンポーネントファイルを作成。
まずはコンポーネントを表示することを目的とするので最小限のコードのみ記述。

/views/member/MemberDetail.vue
<template>
  <h1>会員管理</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>
        <!-- Top画面に戻るリンク。ReouteLinkタグ構文を利用 -->
        <RouterLink v-bind:to="{ name: 'AppTop' }"> TOP </RouterLink>
      </li>
      <li>
        <!-- 会員リスト画面に戻るリンク。ReouteLinkタグ構文を利用 -->
        <RouterLink v-bind:to="{ name: 'MemberList' }"> 会員リスト </RouterLink>
      </li>
      <li>会員詳細情報</li>
    </ul>
  </nav>
  <section>
    <h2>会員詳細情報</h2>
  </section>
</template>

index.tsに会員詳細画面を表示させるルーティング情報を登録。

import { createRouter, createWebHistory } from 'vue-router'
// routes 配列の型定義
import type { RouteRecordRaw } from 'vue-router'
import AppTop from '@/views/AppTop.vue'

const routeSettings: RouteRecordRaw[] = [
  { // AppTop
    path: '/',
    name: 'AppTop',
    component: AppTop,
  },
  { // MemberList
    path: '/member/memberList',
    name: 'MemberList',
    component: () => {
      return import('@/views/member/MemberList.vue')
    },
  },
  { // MemberDetail
    path: '/member/detail/:id',
    name: 'MemberDetail',
    component: () => {
      return import('@/views/member/MemberDetail.vue')
    },
  },
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: routeSettings,
})

export default router

MemberDetailのルーティング登録コードの Pahth プロパティの末尾が「:id」になっている。
このようなパス中のコロンで始まる部分は固定のパス文字列ではなくルートパラメータとして扱われるようになる。

const routeSettings: RouteRecordRaw[] = [
  {
    path: "/⋯/:ルートパラメータ名",
      ︙
  },
   ︙
];

8.3.2 ルートパラメータの埋め込み

router/index.ts の追記によりルートパラメータを利用したルーティングの登録が終わったので、各リンクにおいてルートパラメータを埋め込む必要がある。
MemberList.vueの下記の記述がそれになる。

<RouterLink v-bind:to="{ name: 'MemberDetail', params: { id: id } }">
v-bind:to="{
  name: 'ルーティング名',
  params: {
    ルートパラメータ名: 値,
     ︙
  }
}"
<ul>
  <li>
    <a href="/member/detail/33456" class=""> 
      IDが33456の田中太郎さん 
    </a>
  </li>
  <li>
    <a href="/member/detail/47783" class=""> 
      IDが47783の鈴木二郎さん 
    </a>
  </li>
</ul>

8.3.3 ルートパラメータを Props として取得

パラメータを渡す準備が整ったので、遷移先である MemberDeteail コンポーネントにルートパラメータを受け取るコードを記述。

views/member/MemberDetail.vue
<script setup lang="ts">
import { inject, computed } from 'vue'
import { RouterLink } from 'vue-router'
import type { Member } from '@/interfaces'

// Props定義を行うだけで、そのPropsに自動的にルートパラメータが格納される。
interface Props {  // 1.
  id: number
}
const props = defineProps<Props>()

const memberList = inject('memberList') as Map<number, Member>
const member = computed((): Member => {
  return memberList.get(props.id) as Member
})
const localNode = computed((): string => {
  let localNote = '---'
  if (member.value.note != undefined) {
    localNote = member.value.note
  }
  return localNote
})
</script>

<template>
  <h1>会員管理</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>
        <RouterLink v-bind:to="{ name: 'AppTop' }"> TOP </RouterLink>
      </li>
      <li>
        <RouterLink v-bind:to="{ name: 'MemberList' }"> 会員リスト </RouterLink>
      </li>
      <li>会員詳細情報</li>
    </ul>
  </nav>
  <section>
    <h2>会員詳細情報</h2>
    <dl>
      <dt>ID</dt>
      <dd>{{ member.id }}</dd>
      <dt>名前</dt>
      <dd>{{ member.name }}</dd>
      <dt>メールアドレス</dt>
      <dd>{{ member.email }}</dd>
      <dt>備考</dt>
      <dd>{{ localNode }}</dd>
    </dl>
  </section>
</template>
  1. ルートパラメータを受け取る側のコンポーネントでは Props 定義を行うだけでそのPropsに自動的にルートパラメータが格納される

Propsを利用する場合、Propsとして渡す設定コードを router/index.ts に記述する必要がある。

import { createRouter, createWebHistory } from 'vue-router'
// routes 配列の型定義
import type { RouteRecordRaw } from 'vue-router'
import AppTop from '@/views/AppTop.vue'

const routeSettings: RouteRecordRaw[] = [
  {
    // AppTop
    path: '/',
    name: 'AppTop',
    component: AppTop,
  },
  {
    // MemberList
    path: '/member/memberList',
    name: 'MemberList',
    component: () => {
      return import('@/views/member/MemberList.vue')
    },
  },
  {
    // MemberDetail
    path: '/member/detail/:id',
    name: 'MemberDetail',
    component: () => {
      return import('@/views/member/MemberDetail.vue')
    },
    props: (routes) => { // Propsとして値を渡す設定コード
      const idNum = Number(routes.params.id)
      return {
        id: idNum,
      }
    },
  },
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: routeSettings,
})

export default router
const routeSettings: RouteRecordRaw[] = [
  {
    path: "/⋯/:ルートパラメータ名",
      ︙
    props: true
  },
   ︙
]

props プロパティを追記し、値として true を設定するだけで利用できるが、この場合はルートパラメータ名と同名 Props に対し文字列(string型)として値が格納される。
Propsが数値型の場合は渡す側で数値に変換する必要がある。

const routeSettings: RouteRecordRaw[] = [
  {
    path: "/⋯/:ルートパラメータ名",
      ︙
    props: (routes) => {
      routes.params を利用した加工処理
       ︙
      return {
        ルートパラメター名: 加工された値
         ︙
      }
    },
   ︙
]

8.4 スクリプトブロックでのルーティング制御

8.4.1 会員情報追加画面の作成

会員リスト画面に会員情報追加画面へのリンクを作成

<script setup lang="ts">
import { inject } from 'vue'
import { RouterLink } from 'vue-router'
import type { Member } from '@/interfaces'

const memberList = inject('memberList') as Map<number, Member>
</script>

<template>
  <h1>会員管理</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>
        <RouterLink v-bind:to="{ name: 'AppTop' }"> TOP </RouterLink>
      </li>
      <li>会員リスト</li>
    </ul>
  </nav>
  <section>
    <h2>会員リスト</h2>
    <p>新規登録は<RouterLink v-bind:to="{ name: 'MemberAdd' }">こちら</RouterLink>から</p>
    <section>
      <ul>
        <li v-for="[id, member] in memberList" v-bind:key="id">
          <RouterLink v-bind:to="{ name: 'MemberDetail', params: { id: id } }">
            IDが{{ id }}の{{ member.name }}さん
          </RouterLink>
        </li>
      </ul>
    </section>
  </section>
</template>

設定したリンクに該当するルーティング名 MemberAdd のルーティング情報を index.ts に追記

import { createRouter, createWebHistory } from 'vue-router'
// routes 配列の型定義
import type { RouteRecordRaw } from 'vue-router'
import AppTop from '@/views/AppTop.vue'

const routeSettings: RouteRecordRaw[] = [
  {
    // AppTop
    path: '/',
    name: 'AppTop',
    component: AppTop,
  },
  {
    // MemberList
    path: '/member/memberList',
    name: 'MemberList',
    component: () => {
      return import('@/views/member/MemberList.vue')
    },
  },
  {
    // MemberDetail
    path: '/member/detail/:id',
    name: 'MemberDetail',
    component: () => {
      return import('@/views/member/MemberDetail.vue')
    },
    props: (routes) => {
      const idNum = Number(routes.params.id)
      return {
        id: idNum,
      }
    },
  },
  {
    // MemberAdd
    path: '/member/add',
    name: 'MemberAdd',
    component: () => {
      return import('@/views/member/MemberAdd.vue')
    }
  }
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: routeSettings,
})

export default router

8.4.2 ルーティングを制御する Router オブジェクト

MemberAdd コンポーネント

views/member/MemberAdd.vue
<script setup lang="ts">
import { inject, reactive } from 'vue'
import { RouterLink, useRouter } from 'vue-router' // 1. userRouterをインポート
import type { Member } from '@/interfaces'

const router = useRouter() // 2. Ruterオブジェクトを取得する関数
const memberList = inject('memberList') as Map<number, Member> // 3.Inject

// 4. リアクティブなMemberオブジェクトを用意
const member: Member = reactive({
  id: 0,
  name: '',
  email: '',
  points: 0,
  note: '',
})

// 5. [登録]ボタンクリックで呼び出されるメソッド
const onAdd = (): void => {
  console.log(member)
  memberList.set(member.id, member) // 6. Memberオブジェクトを追加
  router.push({ name: 'MemberList' }) // 7. リスト画面に遷移
}
</script>

<template>
  <h1>会員管理</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>
        <RouterLink v-bind:to="{ name: 'AppTop' }"> AppTop </RouterLink>
      </li>
      <li>
        <RouterLink v-bind:to="{ name: 'MemberList' }"> 会員リスト </RouterLink>
      </li>
      <li>会員情報追加</li>
    </ul>
  </nav>
  <section>
    <h2>会員情報追加</h2>
    <p>情報を入力し、登録ボタンをクリックしてください。</p>

    <!-- 8. required属性による入力チェックやinput要素のnumberタイプやemailタイプによる -->
    <!--    入力チェックを有効にするため、ボタンのタイプをsubmitにしている。            -->
    <!--    イベント処理はfrom の v-on:submit で行っているので、buttonのsubmitを      -->
    <!--    キャンセルするために .prevent修飾子を利用している。                       -->
    <form v-on:submit.prevent="onAdd">
      <!-- 9. 各プロパティと入力コントロールはそれぞれのタグにあるようにv-modelで同期されている -->
      <dl>
        <dt><label for="addId">ID </label></dt>
        <dd><input type="number" id="addId" v-model.number="member.id" required /></dd>
        <dt><lavel for="addName">名前 </lavel></dt>
        <dd><input type="text" id="addName" v-model="member.name" required /></dd>
        <dt><lavel for="addEmail">メールアドレス </lavel></dt>
        <dd><input type="email" id="addEmail" v-model="member.email" required /></dd>
        <dt><lavel for="addPoints">保有ポイント </lavel></dt>
        <dd><input type="number" id="addPoints" v-model="member.points" required /></dd>
        <dt><lavel for="addNote">備考 </lavel></dt>
        <dd><textarea id="addNote" v-model="member.note" required></textarea></dd>
      </dl>
      <button type="submit">登録</button>
    </form>
  </section>
</template>

入力データを会員リストデータに追加する

データの追加は[登録]ボタンクリックで呼び出される onAdd メソッドの中の 6. で実行。
あらかじめ 3. で Inject した Map オブジェクトである MemberList に set() メソッドを使ってMember オブジェクトを追加している。
そもそも memberList は App.vue で Provide される際に reactive() 関数を利用してリアクティブになっているので、それ以降データの追加・削除・改変がそのまま反映されるようになっている。

リスト画面に遷移する

Vue Router ではスクリプトブロック内でルーティングを制御するためのオブジェクトとして Router オブジェクトを提供している。
この Router オブジェクトを取得する関数が 2. の useRouter() である。
この関数を利用するために 1. でインポートしている。

Router オブジェクトのメソッド push() を利用すると、引数で渡したパスに表示が遷移する。
引数には RouterLink タグの v-bind:to ディレクティブの属性値と同じものを記述する。
router.push({ name: ‘MemberList’ })

また、遷移する際にルートパラーメータを埋め込むことも可能である。
router.push({name: ‘MemberDetail”, params: {id: member.id}})
このコードに置き換えた場合、[登録]ボタンクリック後に会員詳細情報画面へ遷移し、登録したばかりの会員情報が表示される。

8.4.3 Router オブジェクトの他のメソッド

Ruter オブジェクトには Push() 以外にもメソッドがある。

メソッド内容
push()指定パスに遷移する
replace()現在のパスを置き換える
back()履歴上のひとつ前の画面に戻る
forward()履歴上のひとつ次の画面に進む
go()履歴上の指定の画面に進む

replace()メソッドは現在の表示画面を置き換える。引数の書式は push() と同じで見た目の挙動も同じである。
違いは、push()の場合画面の遷移が履歴としてブラウザに記録され、ブラウザの戻るボタンで戻ることが出来るようになり、replace()の場合は履歴に残らないのでブラウザの戻るボタンで戻ることが出来なくなる。

go() には引数として何画面分履歴を移動するかを指定する。
例えば go(-2) とすると2画面分戻る。
back()はgo(-1)、forward()はgo(1)と同じ。

8.5 Vue router のその他の機能

8.5.1 スクリプトブロックでのルーティング情報の取得

ルートパラメータをスクリプトブロックで取得したい場合、useRoute()関数で取得したオブジェクトを利用する。

inport {useRoute} from "vue-router"
 ・
 ・
const route = useRoute()
const idNo = route.params.id

MemberDetail.vueで、userRoute()関数を使ってみる。

MemberDetail.vue
<script setup lang="ts">
import { inject, ref , computed, watchEffect } from 'vue'
import { RouterLink, useRoute} from 'vue-router'  // 1.useRoute関数利用
import type { Member } from '@/interfaces'

/*
interface Props {
  id: number
}
const props = defineProps<Props>()
const memberList = inject('memberList') as Map<number, Member>
const member = computed((): Member => {
  return memberList.get(props.id) as Member
})
*/
// ルートパラメータを取得
// useRoute()関数で取得したオブジェクトを利用する
const route = useRoute()
let id = Number(route.params.id)  // パラメータを取得

// Provideされたデータをinject
const memberList = inject('memberList') as Map<number, Member>
const member = ref(memberList.get(id) as Member)  // idに対応するデータを取得

const localNode = computed((): string => {
  let localNote = '---'
  if (member.value.note != undefined) {
    localNote = member.value.note
  }
  return localNote
})

</script>

<template>
  <h1>会員管理</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>
        <RouterLink v-bind:to="{ name: 'AppTop' }"> TOP </RouterLink>
      </li>
      <li>
        <RouterLink v-bind:to="{ name: 'MemberList' }"> 会員リスト </RouterLink>
      </li>
      <li>会員詳細情報</li>
    </ul>
  </nav>
  <section>
    <h2>会員詳細情報</h2>
    <dl>
      <dt>ID</dt>
      <dd>{{ member.id }}</dd>
      <dt>名前</dt>
      <dd>{{ member.name }}</dd>
      <dt>メールアドレス</dt>
      <dd>{{ member.email }}</dd>
      <dt>備考</dt>
      <dd>{{ localNode }}</dd>
    </dl>
  </section>
</template>

routeオブジェクトは RuteLocationNormalized型であり、主なプロパティには下記のものがある。
これらを利用することで現在のルーティングうに関するさまざまな情報を取得できる。

プロパティ内容
name ルーティング名MemberDetail
fullPathpath, hash, query の全てが含まれたパス文字列/member/detail/47783#section?name=tanaka
pathルーティングパス文字列/member/detail/47783
hashハッシュ(#以降の文字列)#section
queryクエリ情報(?以降の情報){name:tanaka}
paramsルートパラメータ{id:47783}

8.5.2 さまざまなルートパラメータ

複数パラメータ

  • パス設定
    • path: “/member/search/:name/:points”
  • アクセスURL
    • http://localhost:3000/member/search/suzuki/45
  • route.paramsのプロパティに入る値
    • nameプロパティに「sukuki」
    • pointsプロパティに「45」

省略可能パラメータ

  • パス設定
    • path:// “/member/show/:name/:points?”
  • pointsパラメータは省略可
    • http://localhost:3000/member/show/suzuki/45
    • http://localhost:3000/member/show/tanaka

可変長パラメータ

  • [*]: ルートパラメータが全くない状態も受け付ける
    • パス設定
      • path: “/member/call/:id*
    • 次のどちらのURLも作動する
      1. http://localhost:3000/member/call
      2. http://localhost:3000/member/call/14/25/65
    • 結果
      1. なにも入らない
      2. route.params.idに配列[14, 25, 65]が格納される
  • [+]: 必ずひつとはルートパラメータを記述する必要がある
    • パス設定
      • path: “member/call/id+
    • URL
      1. http://localhost:3000/member/tell
      2. http://localhost:3000/member/tell/14/25/65
    • 結果
      1. エラーになる
      2. route.params.idに配列[14, 25, 65]が格納される

値のチェック

  • パス設定:正規表現を使って値のチェックが行える
    • 5桁の数字チェック
      • path: “member/pull/:id(\\d{5})”
  • URL
    1. http://localhost:3000/member/pull/98765
    2. http://localhost:3000/member/pull/1234
    3. http://localhost:3000/member/pull/hello
  • 結果
    1. 問題なく動作
    2. 桁数不足としてエラーとなる
    3. 文字列なのでエラーとなる

404 Not Found

ルートパラメータの仕組みを利用すると、存在しないURLにアクセスした場合に特定の画面を表示するルーティング設定「404 Not Found」の実現が可能

path: "/:pathMatch(.*)*",
name: "NotFound",
component: () => {
  return inport("@/views/NotFound.vue")
}

ルートである「/」以降全てがルートパラメータになっている。
(.*)は正規表現のワイルドカードでどんな文字列にも一致する。
末尾の*は繰り返しを意味し、階層(スラッシュ)を無視して深い階層もすべて拾う。

URLマッチングの優先順位は具体的(厳密)にマッチするルートが優先されるので、曖昧な「”/:pathMatch(.)“」は最終手段として扱われる。

結果、マッチするルートが無いURLのみ取得される。

pathMatchという名前の変数に入力されたURLが配列として格納される。
URLの階層(/で区切られた部分)ごとに配列の要素として分割されて格納される。

import { useRoute } from 'vue-router'

const route = useRoute()
// 配列として取得できる
console.log(route.params.pathMatch) 

8.5.3 ネストされたルーティング

ルーティングは入れ子(ネスト)にできる。
会員管理画面で会員リストの会員をクリックすると会員管理画面に遷移していたが、これを会員管理画面の会員リストの下部に表示領域を設けて、そこに会員詳細情報を表示させることもできる。

今までの画面遷移の場合、会員リストをクリックすると、

会員管理

TOP>会員リスト

会員リスト

新規登録はこちらから

  • IDが33456の田中太郎さん ←これをクリック
  • IDが47783の鈴木二郎さん

会員管理画面が開く

会員管理

TOP>会員リスト>会員詳細情報

会員詳細情報

ID
  33456
名前
  田中太郎
メールアドレス
  bou@example.com
備考
  初回入会特典あり

ネストにして下部に表示させると、入れ子になる。

会員管理

TOP>会員リスト

会員リスト

新規登録はこちらから

  • IDが33456の田中太郎さん ←これをクリックすると、下部に表示される↓
  • IDが47783の鈴木二郎さん

会員管理

TOP>会員リスト>会員詳細情報

会員詳細情報

ID
  33456
名前
  田中太郎
メールアドレス
  bou@example.com
備考
  初回入会特典あり

入れ子にするには、ルーティング登録情報のMemberList設定にchildrenプロパティを追加する。

index.ts
import { createRouter, createWebHistory } from 'vue-router'
// routes 配列の型定義
import type { RouteRecordRaw } from 'vue-router'
import AppTop from '@/views/AppTop.vue'

// createRouter({})の「routes: []」で直接記述されていな配列を分離したもの。
const routeSettings: RouteRecordRaw[] = [
  {
    // AppTop
    // URLがルート(/)パスの際にルーティング表示領域に表示する設定
    path: '/',
    name: 'AppTop',
    component: AppTop,
  },
  {
    // MemberList
    path: '/member/memberList',
    name: 'MemberList',
    component: () => {
      return import('@/views/member/MemberList.vue')
    },























    ]
  },
  {
    // MemberDetail
    path: '/member/detail/:id',
    name: 'MemberDetail',
    component: () => {
      return import('@/views/member/MemberDetail.vue')
    },
    props: (routes) => {
      const idNum = Number(routes.params.id)
      return {
        id: idNum,
      }
    },
  },
  {
    // MemberAdd
    path: '/member/add',
    name: 'MemberAdd',
    component: () => {
      return import('@/views/member/MemberAdd.vue')
    },
  },
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: routeSettings,
})

export default router
index.ts
import { createRouter, createWebHistory } from 'vue-router'
// routes 配列の型定義
import type { RouteRecordRaw } from 'vue-router'
import AppTop from '@/views/AppTop.vue'

// createRouter({})の「routes: []」で直接記述されていな配列を分離したもの。
const routeSettings: RouteRecordRaw[] = [
  {
    // AppTop
    // URLがルート(/)パスの際にルーティング表示領域に表示する設定
    path: '/',
    name: 'AppTop',
    component: AppTop,
  },
  {
    // MemberList
    path: '/member/memberList',
    name: 'MemberList',
    component: () => {
      return import('@/views/member/MemberList.vue')
    },
    children: [
      {
        // MemberDetail
        path: "detail/:id",
        name: "MemberDetail",
        component: () => {
          return import('@/views/member/MemberDetail.vue')
        },
        props: (routes) => {
          const idNum = Number(routes.params.id)
          return {
            id: idNum,
          }        
        }
      },
      {
        // MemberAdd
        path: 'add',
        name: 'MemberAdd',
        component: () => {
          return import('@/views/member/MemberAdd.vue')
        },
      },
    ]
  },






















]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: routeSettings,
})

export default router

上記サンプルでリストをクリックして会員詳細情報画面を表示させた後、他のメンバをクリックすると、URLとしてはルートパラメータ部分のみが変化する。
この場合、MemberDetailコンポーネントが再利用されてしまい、詳細が更新されない。

http://localhost:3000/member/memberList/detail/33456

http://localhost:3000/member/memberList/detail/47783
← ルートパラメータのみが変化

解決方法は、watch や watchEffect を使ってデータを再取得するようにする。
MemberDetail.vueに watchEffectを追加してみた。

<script setup lang="ts">
import { inject, ref , computed, watchEffect } from 'vue'
import { RouterLink, useRoute} from 'vue-router'  // 1.useRoute関数利用
import type { Member } from '@/interfaces'

// ルートパラメータを取得
// useRoute()関数で取得したオブジェクトを利用する
const route = useRoute()
let id = Number(route.params.id)  // パラメータを取得

// Provideされたデータをinject
const memberList = inject('memberList') as Map<number, Member>
const member = ref(memberList.get(id) as Member)  // idに対応するデータを取得

watchEffect(() => {
  const id = Number(route.params.id)
  member.value = memberList.get(id) as Member
})

const localNode = computed((): string => {
  let localNote = '---'
  if (member.value.note != undefined) {
    localNote = member.value.note
  }
  return localNote
})

</script>

<template>
  <h1>会員管理</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>
        <RouterLink v-bind:to="{ name: 'AppTop' }"> TOP </RouterLink>
      </li>
      <li>
        <RouterLink v-bind:to="{ name: 'MemberList' }"> 会員リスト </RouterLink>
      </li>
      <li>会員詳細情報</li>
    </ul>
  </nav>
  <section>
    <h2>会員詳細情報</h2>
    <dl>
      <dt>ID</dt>
      <dd>{{ member.id }}</dd>
      <dt>名前</dt>
      <dd>{{ member.name }}</dd>
      <dt>メールアドレス</dt>
      <dd>{{ member.email }}</dd>
      <dt>備考</dt>
      <dd>{{ localNode }}</dd>
    </dl>
  </section>
</template>

memberのデータの編集を行いたい場合、watchEffectを使うとバグの原因になるようです。
編集でフォームを入力している最中に無関係な理由で watchEffectが再実行され、入力中の内容が消えて元に戻ってしまうという「先祖返りバグ」が起きるようです。
編集画面では、 watchEffect ではなく watch (明示的な監視)を使い「URL(ID)が変わった瞬間だけ編集用コピーを作成する」という設計にするのが鉄則だそうです。

MemberDetailEdit.vue
<script setup lang="ts">
import { inject, ref, computed, watch } from 'vue'  // watchを利用
import { RouterLink, useRoute, useRouter } from 'vue-router'
import type { Member } from '@/interfaces'

const route = useRoute()    // ルートパラメータを取得
const router = useRouter()  // 保存後に一覧に戻るなどの画面遷移用
const memberList = inject('memberList') as Map<number, Member>

// 1. 画面の入力フォームと同期する「編集用の状態」を用意
const memberForm = ref<Member>({
  id: 0,
  name: '',
  email: '',
  points: 0,
  note: ''
})

// 2. watch を使い、URLのIDが変わった時だけマスターデータから編集用コピーを作成
// ・第1引数はリアクティブなオブジェクトを渡すルール
//   route.params.idはただの値となるので、アロー関数で包んで渡す。
//   vueは「値が変わったかどうかを調べる時に毎回この関数を実行して最新の値を
//   チェックすればいいんだな」と理解する。
// ・第2引数は、newIdで新しい値を受け取っている
watch(
  () => route.params.id,
  (newId) => {
    const id = Number(newId)
    const masterData = memberList.get(id)
    
    if (masterData) {
      // 参照を切り離すためにオブジェクトをコピー(シャローコピー)
      memberForm.value = { ...masterData }
    }
  },
  { immediate: true } // コンポーネント読み込み時にも実行
)

// 3. 備考の表示用算出プロパティ(memberForm を参照するように変更)
const localNote = computed((): string => {
  return memberForm.value.note && memberForm.value.note.trim() !== '' 
    ? memberForm.value.note 
    : '---'
})

// 4. 保存ボタンが押された時の処理
const onSave = () => {
  // inject された Map オブジェクトを直接更新(リアクティブに変更が伝播します)
  memberList.set(memberForm.value.id, { ...memberForm.value })
  alert('変更を保存しました')
  
  // 必要に応じて保存後に一覧画面へ戻す場合
  router.push({ name: 'MemberList' })
}
</script>

<template>
  <h1>会員管理</h1>
  <nav id="breadcrumbs">
    <ul>
      <li>
        <RouterLink v-bind:to="{ name: 'AppTop' }"> TOP </RouterLink>
      </li>
      <li>
        <RouterLink v-bind:to="{ name: 'MemberList' }"> 会員リスト </RouterLink>
      </li>
      <li>会員編集</li>
    </ul>
  </nav>
  
  <section>
    <h2>会員詳細情報(編集)</h2>
    <!-- submit.prevent でリロードを防ぎ、保存処理を実行 -->
    <form @submit.prevent="onSave">
      <dl>
        <dt>ID</dt>
        <!-- IDは変更できないように readonly に設定 -->
        <dd>{{ memberForm.id }}</dd>
        
        <dt><label for="edit-name">名前</label></dt>
        <dd>
          <input id="edit-name" type="text" v-model="memberForm.name" required />
        </dd>
        
        <dt><label for="edit-email">メールアドレス</label></dt>
        <dd>
          <input id="edit-email" type="email" v-model="memberForm.email" required />
        </dd>
        
        <dt><label for="edit-points">ポイント</label></dt>
        <dd>
          <input id="edit-points" type="number" v-model.number="memberForm.points" required />
        </dd>
        
        <dt><label for="edit-note">備考</label></dt>
        <dd>
          <textarea id="edit-note" v-model="memberForm.note"></textarea>
          <p class="note-preview">現在のプレビュー: {{ localNote }}</p>
        </dd>
      </dl>
      
      <div class="actions">
        <button type="submit">保存する</button>
        <RouterLink v-bind:to="{ name: 'MemberList' }" class="btn-cancel">キャンセル</RouterLink>
      </div>
    </form>
  </section>
</template>

<style scoped>
/* スタイル調整(必要に応じて追加・変更してください) */
dl dt {
  font-weight: bold;
  margin-top: 10px;
}
input, textarea {
  width: 100%;
  max-width: 400px;
  padding: 6px;
  box-sizing: border-box;
}
textarea {
  height: 80px;
}
.note-preview {
  font-size: 0.85rem;
  color: #666;
  margin-top: 4px;
}
.actions {
  margin-top: 20px;
}
button {
  margin-right: 10px;
  padding: 8px 16px;
  cursor: pointer;
}
.btn-cancel {
  display: inline-block;
  padding: 8px 16px;
  background: #eee;
  color: #333;
  text-decoration: none;
  border-radius: 4px;
}
</style>

8.5.4 マルチビュー

複数の RouterView タグを並列に記述して、それぞれのコンポーネントを表示させる方法。

App.vue

RowterView タグに name 属性を記述して表示領域に名前を付ける。

<script setup lang="ts">
import { RouterView } from 'vue-router'
</script>

<template>
  <header>
    <h1>マルチビューサンプル</h1>
  </header>
  <main>
    <RouterView/> <!-- デフォルト -->
  </main>
  <section id="sub">
    <RouterView name="sub"/> <!-- 名前を指定 -->
  </section>
</template>

<style>
main {
	border: blue 1px solid;
	padding: 10px;
}
#sub {
	border: orange 1px solid;
	padding: 10px;
}
</style>

index.ts

コンポーネントを登録するプロパティが conponents と複数形になる。
値としては、それぞれの RouteView 部分に表示するコンポーネントをオブジェクトとして記述する。

ここでは、<RouterView/> のように name 属性が無い RouterView タグにレンダリングするコンポーネントは default というプロパティで指定している。
<RouterView name=”sub”>のように name 属性がある RouterView タグに対してはその name 属性をプロパティ名とする。

import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
import AppTop from "@/views/AppTop.vue"
import Sub from "@/views/Sub.vue"

const routerSettings: RouteRecordRaw[] = [
  {
    path: "/",
    name: "AppTop",
    components: {
      default: AppTop,
      sub: Sub
    }
  }
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: routerSettings,
})

export default router

コンポーネント

AppTop.vue
<template>
    <h1>TOP</h1>
    <section>
        <p>メインコンテンツです。</p>
    </section>
</template>
Sub.vue
<template>
    <p>サブコンテンツです。</p>
</template>

実行結果

マルチビューサンプル

TOP

メインコンテンツです。

サブコンテンツです。

8.5.5 ナビゲーションガード

Vue Router では画面遷移が発生する前後に処理をはさむことができ、これをナビゲーションガードという。

Routerオブジェクトのガードメソッド

カテゴリの「グローバル」は全てのルーティングに適用されるガード。

カテゴリガードメソッド記述先
グローバル
(全てのルーティングに適用)
beforeEachrouter/index.ts
beforeResolve
afterEach
ルーティングごとbeforeEnter
コンポーネントごとbeforeRouteEnter各コンポーネント内
beforeRouterEnter
beforeRouteLeave

ナビゲーションガードの呼び出し順番

左側の「リンクをクリック」から始まる流れが、完全に画面が遷移する場合の順序。

http://localhost:3000/member/memberList

http://localhost:3000/member/detail/33456

右側の「ルートパラメータ違いのリンクをクリック」は、ルートパラメータのみが変化する場合の流れで、画面表示用コンポーネントが再利用されるため、呼び出されるガードが違う。

http://localhost:3000/member/detail/33456

http://localhost:3000/member/detail/47783

グローバルガードでの定義方法

router/index.ts 内では、createRouter() で router オブジェクトを取得した後、その router オブジェクトを export するまでの間に router オブジェクトのそれぞれのメソッドを実行する形で設定する。

const router = createRouter({
  ︙
});
router.beforeEach(
  (to, form) => {
    ︙
  }
);
  ︙
export default router

ルーティングごとのガードの設定方法

各ルーティング設定情報に beforeEnter プロパティとして設定する。

{
  path: "/",
  ︙
  beforeEnter: (to, form) => {
    ︙
  }
}

コンポーネントごとのガードの設定方法

クスリプとブロック内で onBeforeRouteLeave() 関数と onBeforeRouterUpdate() 関数を利用して設定する。

<secript setup lang="ts">
import {onBeforeRouteLeave, onBeforeRouterUpdate} from "vue-router";
 ︙
onBeforeRouterLeave(
  (to, from) => {
    ︙
  }
);
onBeforeRouteUpdate(
  (to, form) => {
    ︙
  }
);

ナビゲーションガード設定の引数

グローバル、ルーティングごと、コンポーネントごとのいずれのガードであっても、引数やプロパティの値として渡すものは同一である。

(to, form) => {
  挿入したい処理
}

引数の to と from はどちらも RouteLocationNormalized オブジェクトで、ルーティング情報が格納されたオブジェクトである。
to がこれから遷移する先の情報で、form が現在の情報。
これらのオブジェクトの fullPath プロパティや params プロパティなどを参照すると、各種データを取り出せるので、それらを利用して画面遷移の前後の適切なタイミングに挿入したい処理を記述する。

8.5.6 リダイレクト

単純に特定のパスにリダイレクトする場合は redairectを利用する。

{
  path: "/member/go",
  name: "Go",
  redairect: {
    name: "MemberCall"
  }
}

リダイレクト先を動的に制御したい場合は、redairect プロパティの値としてアロー式を記述する。

{
  path: "/member/goDynamic/:id",
  name "GoDynamic",
  redirect: (to) => {
    return {
      name: "MemberCall",
      params: {id: to.params.id}
    }
  }
}

アロー式の引数である to は現在のルーティング情報が格納された RouteLocationNormalized オブジェクトなので、そのデータを活用しつつ最終的に RouterLink タグの v-bind:to ディレクティブの属性値と同一の書式のオブジェクトをリターンする。

コメント

タイトルとURLをコピーしました