- Vue.js
Vue Routerの使い方|v5対応・ガードと404を実行結果つきで
Vue Routerとは、URLに応じて表示するコンポーネントを切り替える、Vue公式のルーティングライブラリです。 ページを再読み込みせずに画面を切り替えるSPA(シングルページアプリケーション)を作るときに使います。2026年9月時点の最新版は Vue Router 5.3.1 で、Vue 3 と組み合わせて使います。
この記事では、routes 配列で書く従来の方法と、v5で本体に統合されたファイルベースルーティング(src/pages のファイルからルートを自動生成し、ルート名に型が付く方式)の両方をコピペで動くコードで解説します。さらに、Chromium で実際にリンクをクリックしたときのURLと画面の結果、ガードでの転送、404、存在しないルート名のエラー文、遅延読み込みで分かれたビルド出力をそのまま載せています。構成は Vite を使う現在の標準の形です。
Vue Routerの早見表
| やりたいこと | 書き方(Vue Router 5) |
|---|---|
| ルーターを作る | createRouter({ history: createWebHistory(), routes }) |
| アプリに登録する | createApp(App).use(router) |
| リンクを置く | <RouterLink to="/about">概要</RouterLink> |
| 表示場所を決める | <RouterView /> |
| URLの一部を受け取る(動的ルート) | path: '/items/:id' → route.params.id |
| 数字だけ受け付ける | path: '/items/:id(\\d+)' |
?q=... を読む・書く |
route.query.q / router.replace({ query: { q } }) |
| 名前で遷移する | { name: 'item', params: { id: 42 } } |
| JSから遷移する | router.push(...) / router.replace(...) / router.back() |
| 親子の画面(ネスト) | children: [...] + 親に <RouterView /> |
| params を props で受け取る | ルートに props: true |
| 全ページ共通のチェック | router.beforeEach((to) => {...}) |
| 特定ルートだけチェック | ルートに beforeEnter |
| 画面を離れる前に確認 | onBeforeRouteLeave(() => false) |
| ページごとに分割して読み込む | component: () => import('./views/X.vue') |
| 404ページ | path: '/:pathMatch(.*)*' を最後に置く |
| 遷移時のスクロール位置 | scrollBehavior(to, from, saved) {...} |
| ファイルからルートを自動生成(v5) | vite.config に VueRouter()(vue-router/vite) |
ガードは「値を返す」書き方が標準です。return false で中止、return { name: 'login' } で転送、何も返さなければ進みます。第3引数の next() は v5 の型定義で非推奨です。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Vue Routerの使い方|v5対応・ガードと404を実行結果つきで」
検証環境
| 項目 | バージョン |
|---|---|
| Node.js | 22.22.2 |
| vue | 3.5.43 |
| vue-router | 5.3.1 |
| vite / @vitejs/plugin-vue | 8.3.1 / 6.0.9 |
| 型チェック | typescript 6.0.3 + vue-tsc 3.3.11 |
| 画面確認 | Playwright 1.56.1 + Chromium |
新規なら npm create vue@latest で「Router」を選ぶと同じ構成が作られます。既存のプロジェクトには次のコマンドで追加します。
npm i vue-router@5
v4からv5で変わったこと
Vue Router 5(5.0.0 は2026年1月29日公開)は、別パッケージだった unplugin-vue-router を本体に統合した移行用のリリースです。公式の移行ガイドでは、「unplugin-vue-router を使っていない v4 ユーザーには破壊的変更がない」とされています。
| 項目 | v4 | v5 |
|---|---|---|
routes 配列で書く従来の方法 |
○ | そのまま動く |
| ファイルベースルーティング | 別パッケージ unplugin-vue-router |
本体の vue-router/vite |
| 自動生成ルートの読み込み | vue-router/auto-routes(unplugin経由) |
vue-router/auto-routes |
| 型付きルート(ルート名の補完) | unplugin が必要 | 本体だけで生成(route-map.d.ts) |
| データローダー | unplugin-vue-router/data-loaders/basic |
vue-router/experimental(実験的) |
ガードの next() |
使える(ドキュメントは値を返す書き方を推奨) | 型定義で @deprecated、値を返す書き方へ |
| peerDependencies の vue | ^3.5.0 |
^3.5.34 || ^4.0.0 |
| IIFEビルド | devtools-api を同梱 | @vue/devtools-api を含まない |
| 次のメジャー(v6) | ― | ESMのみ・非推奨APIを削除予定 |
unplugin-vue-router から移る場合は、import 先を次のように書き換えます(公式移行ガイドより)。
| 旧 | 新 |
|---|---|
unplugin-vue-router/vite |
vue-router/vite |
unplugin-vue-router |
vue-router/unplugin |
unplugin-vue-router/data-loaders/basic |
vue-router/experimental |
/// <reference types="unplugin-vue-router/client" /> |
削除(生成される route-map.d.ts を tsconfig に含める) |
routes配列でルーティングを書く(従来の方法)
まずは、多くのプロジェクトで使われている routes 配列で書く方法です。この1ファイルに、動的ルート・ネスト・ガード・遅延読み込み・404・スクロールをまとめました。
ルーターの定義(コピペ用)
// src/router.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from './views/HomeView.vue'
import { isLoggedIn } from './auth'
const routes = [
{ path: '/', name: 'home', component: HomeView },
// 遅延読み込み:このページを開いたときに別ファイル(チャンク)を読み込む
{ path: '/about', name: 'about', component: () => import('./views/AboutView.vue') },
{ path: '/items', name: 'items', component: () => import('./views/ItemList.vue') },
// 動的ルート::id の部分が params.id になる(\\d+ で数字だけ)
{ path: '/items/:id(\\d+)', name: 'item', component: () => import('./views/ItemDetail.vue'), props: true },
// ネストしたルート:UserLayout の中の <RouterView> に子が表示される
{
path: '/users/:userId',
component: () => import('./views/UserLayout.vue'),
children: [
{ path: '', name: 'user', component: () => import('./views/UserProfile.vue') },
{ path: 'tasks', name: 'user-tasks', component: () => import('./views/UserTasks.vue') },
],
},
{
path: '/admin',
name: 'admin',
component: () => import('./views/AdminView.vue'),
meta: { requiresAuth: true },
},
{
path: '/edit',
name: 'edit',
component: () => import('./views/EditView.vue'),
// ルート単位のガード:クエリに from がないと入れない例
beforeEnter: (to) => {
if (!to.query.from) return { name: 'home' }
},
},
{ path: '/login', name: 'login', component: () => import('./views/LoginView.vue') },
// 404:どのルートにも一致しないパスをすべて受ける
{ path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('./views/NotFound.vue') },
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) return savedPosition // 戻る・進むでは元の位置へ
if (to.hash) return { el: to.hash, behavior: 'smooth' } // #見出し へ
return { top: 0 } // それ以外はページの先頭へ
},
})
// 全体のガード:meta.requiresAuth のページは未ログインならログイン画面へ
router.beforeEach((to) => {
if (to.meta.requiresAuth && !isLoggedIn.value) {
return { name: 'login', query: { redirect: to.fullPath } }
}
})
export default router
// src/auth.js(学習用の簡易版。実務では Pinia のストアに置く)
import { ref } from 'vue'
export const isLoggedIn = ref(false)
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
RouterLink と RouterView(App.vue)
<!-- src/App.vue -->
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
<template>
<nav>
<RouterLink to="/">ホーム</RouterLink> |
<RouterLink :to="{ name: 'about' }">概要</RouterLink> |
<RouterLink :to="{ name: 'items', query: { q: 'ペン' } }">備品検索</RouterLink> |
<RouterLink :to="{ name: 'item', params: { id: 42 } }">備品42</RouterLink> |
<RouterLink :to="{ name: 'user-tasks', params: { userId: 7 } }">ユーザー7のタスク</RouterLink> |
<RouterLink :to="{ name: 'admin' }">管理</RouterLink>
</nav>
<p id="path">現在のパス: {{ route.fullPath }}</p>
<RouterView />
</template>
<style>
/* 現在のページのリンクには router-link-active クラスが付く */
.router-link-active { font-weight: bold; }
</style>
<RouterLink> は <a> タグとして描画され、クリックしてもページ全体を読み込み直しません。 to は 「名前 + params」で書くと、URLの形を変えてもリンク側を直さずに済みます。
動的ルート・クエリ・プログラム遷移
動的ルートを props で受け取る
<!-- src/views/ItemDetail.vue -->
<script setup>
import { useRouter } from 'vue-router'
// ルートに props: true を付けたので params.id が props で届く(文字列)
const props = defineProps({ id: String })
const router = useRouter()
</script>
<template>
<h1 id="title">備品 {{ props.id }} の詳細</h1>
<button id="next" @click="router.push({ name: 'item', params: { id: Number(props.id) + 1 } })">次の備品</button>
<button id="back" @click="router.back()">戻る</button>
</template>
props: true にすると、コンポーネントが useRoute() に依存しなくなり、単体テストや再利用がしやすくなります。 params は常に文字列で届く点に注意してください。
同じルートの中で params だけが変わる(/items/42 → /items/43)とき、コンポーネントは作り直されず再利用されます。 onMounted でデータを取っている場合は2回目が走らないため、watch(() => props.id, ...) で変化に反応させます。
クエリを読み書きする
<!-- src/views/ItemList.vue -->
<script setup>
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const items = ['ボールペン', '蛍光ペン', 'ノート', 'ホチキス']
const q = computed(() => route.query.q ?? '')
const hits = computed(() => items.filter((i) => i.includes(q.value)))
function search(e) {
// 履歴を増やさずにクエリだけ書き換える
router.replace({ query: { q: e.target.value } })
}
</script>
<template>
<h1 id="title">備品一覧</h1>
<input :value="q" @input="search" placeholder="絞り込み" />
<ul id="hits"><li v-for="i in hits" :key="i">{{ i }}</li></ul>
</template>
検索条件をクエリに入れると、URLの共有で同じ絞り込みを再現できます。
push・replace・back の使い分け
| メソッド | 動き | 使う場面 |
|---|---|---|
router.push(to) |
履歴に追加して移動 | ボタンで次の画面へ |
router.replace(to) |
今の履歴を置き換えて移動 | 検索条件の反映、ログイン後の転送 |
router.back() / router.go(-1) |
1つ戻る | 「戻る」ボタン |
ネストしたルート
<!-- src/views/UserLayout.vue -->
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
<template>
<h1 id="title">ユーザー {{ route.params.userId }}</h1>
<RouterLink :to="{ name: 'user' }">プロフィール</RouterLink> |
<RouterLink :to="{ name: 'user-tasks' }">タスク</RouterLink>
<section id="child"><RouterView /></section>
</template>
親コンポーネントの中にもう1つ <RouterView /> を置くと、そこに children のコンポーネントが表示されます。 子ルートの path: '' は、親のURL(/users/7)そのものを開いたときに表示されます。名前付きの子ルートへのリンクでは、親の params(userId)は今の値が引き継がれるので省略できます。
ナビゲーションガードでログインチェック
ガードには3つの置き場所があります。
| 種類 | 書く場所 | 主な用途 |
|---|---|---|
router.beforeEach |
ルーター全体 | ログインチェック、権限、ページタイトル |
beforeEnter |
ルート定義 | そのルートだけの入場条件 |
onBeforeRouteLeave / onBeforeRouteUpdate |
コンポーネント | 未保存の確認、params変化への対応 |
router.afterEach |
ルーター全体 | アクセス解析の送信など(遷移は変えられない) |
ガードの戻り値で遷移が決まります。
| 戻り値 | 結果 |
|---|---|
undefined / true(何も返さない) |
そのまま進む |
false |
遷移を取り消す(今のURLに留まる) |
ルートの位置({ name: 'login' } や '/login') |
そこへ転送する |
| 例外を投げる | 遷移を中止し、router.onError に渡る |
ログイン後に元のページへ戻す
<!-- src/views/LoginView.vue -->
<script setup>
import { useRoute, useRouter } from 'vue-router'
import { isLoggedIn } from '../auth'
const route = useRoute()
const router = useRouter()
function login() {
isLoggedIn.value = true
// beforeEach で付けた redirect に戻す
router.push(route.query.redirect ?? '/')
}
</script>
<template>
<h1 id="title">ログイン</h1>
<button id="login" @click="login">ログインする</button>
</template>
redirect の値は、/ で始まるアプリ内のパスだけを受け付けるようにすると安全です(例:typeof r === 'string' && r.startsWith('/') && !r.startsWith('//'))。外部サイトへ転送されるのを防げます。
未保存の入力があれば離脱を確認する
<!-- src/views/EditView.vue -->
<script setup>
import { ref } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'
const text = ref('')
onBeforeRouteLeave(() => {
if (text.value && !window.confirm('保存していない入力があります。移動しますか?')) {
return false // 遷移を取り消す
}
})
</script>
<template>
<h1 id="title">タスク編集</h1>
<input id="memo" v-model="text" />
</template>
タブを閉じる・再読み込みは対象外なので、ブラウザの beforeunload イベントで別に対応します。
404ページ(catch-all)
<!-- src/views/NotFound.vue -->
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
<template>
<h1 id="title">ページが見つかりません</h1>
<p id="nf">pathMatch: {{ JSON.stringify(route.params.pathMatch) }}</p>
</template>
/:pathMatch(.*)* の末尾の * は「0個以上の区切り」を表し、route.params.pathMatch がパスを / で分けた配列になります。 下の実行結果のとおり、/items/abc のように数字制約(\\d+)に合わないURLも 404 に届きます。
実行結果:Chromiumで遷移させた結果
上のアプリを vite preview --port 7410 で起動し、Playwright で操作して「パス / 見出し」を読み取りました。
初期表示 => 現在のパス: / / ホーム
RouterLink(params) => 現在のパス: /items/42 / 備品 42 の詳細
router.push => 現在のパス: /items/43 / 備品 43 の詳細
router.back => 現在のパス: /items/42 / 備品 42 の詳細
RouterLink(query) => 現在のパス: /items?q=%E3%83%9A%E3%83%B3 / 備品一覧
hits: ボールペン,蛍光ペン
入力でquery更新 => 現在のパス: /items?q=%E3%83%8E / 備品一覧
hits: ノート
ネスト => 現在のパス: /users/7/tasks / ユーザー 7
child: タスク一覧画面
未ログインで管理へ => 現在のパス: /login?redirect=/admin / ログイン
ログイン後 => 現在のパス: /admin / 管理画面
/edit(fromなし) => 現在のパス: / / ホーム
/edit?from=menu => 現在のパス: /edit?from=menu / タスク編集
confirm表示: 保存していない入力があります。移動しますか?
入力ありで離脱→キャンセル => 現在のパス: /edit?from=menu / タスク編集
/items/abc => 現在のパス: /items/abc / ページが見つかりません
pathMatch: ["items","abc"]
/no/such/page => 現在のパス: /no/such/page / ページが見つかりません
pathMatch: ["no","such","page"]
ポイント:
- 日本語のクエリはURLではエンコードされるが、
route.query.qでは元の文字列で読める beforeEachで未ログインの/adminが/login?redirect=/adminに転送され、ログイン後に/adminに戻ったbeforeEnterは条件に合わないと転送(/edit→/)、合えば入場できたonBeforeRouteLeaveでfalseを返すとURLも画面も元のままだった
存在しないルート名・params不足のエラー文
開発サーバー(vite --port 7412)で、間違った router.push を実行したときのエラーです。
router.push({ name: 'nope' })
→ Error: No match for
{"name":"nope","params":{}}
router.push({ name: 'item' }) // params.id を渡していない
→ Error: Missing required param "id"
どちらも Promise の reject ではなく、push を呼んだ時点で例外として投げられました。 本番ビルドではエラー文が短縮されるため、開発中に気づけるよう後述の型付きルートを使うと安心です。
遅延読み込みでチャンクが分かれることを確認
component: () => import(...) と書いたページは、ビルド時に別ファイルに分かれます。実際の vite build の出力です。
✓ 36 modules transformed.
dist/assets/UserTasks-DC4PktO_.js 0.17 kB │ gzip: 0.18 kB
dist/assets/AboutView-DnB-sd-Y.js 0.19 kB │ gzip: 0.19 kB
dist/assets/NotFound-kb5wVF03.js 0.32 kB │ gzip: 0.29 kB
dist/assets/ItemDetail-DpvaA54w.js 0.43 kB │ gzip: 0.34 kB
dist/assets/ItemList-d2i59CSM.js 0.60 kB │ gzip: 0.46 kB
dist/assets/index-BuVltqlJ.js 90.68 kB │ gzip: 35.27 kB
(ほかに UserProfile・AdminView・LoginView・EditView・UserLayout のチャンク。抜粋)
ブラウザで読み込まれたJSも確認しました。
初期表示で読み込んだJS: index-BuVltqlJ.js
「概要」クリックで追加読み込み: AboutView-DnB-sd-Y.js
最初は本体(index-*.js)だけを読み込み、「概要」を開いた時点で AboutView-*.js が追加で読み込まれました。 最初に表示する HomeView は通常の import のまま本体に含め、それ以外を遅延読み込みにするのが基本の形です。
履歴モードとサーバー設定
| 履歴モード | URLの形 | サーバー設定 | 向いている場面 |
|---|---|---|---|
createWebHistory() |
/items/42 |
すべてのパスで index.html を返す設定が必要 | 通常のWebサイト(推奨) |
createWebHashHistory() |
/#/items/42 |
不要 | 設定を変えられない静的ホスティング |
createMemoryHistory() |
URLは変わらない | 不要 | SSR、テスト |
createWebHistory() では、/items/42 を直接開いたときにサーバーがそのパスのファイルを探します。 ビルド結果(dist)を、フォールバック設定のない静的サーバー(python3 -m http.server 7411)と vite preview(フォールバックあり)で配信して比べました。
http://localhost:7411/ → HTTP 200
http://localhost:7411/items/42 → HTTP 404 ← 静的サーバーはファイルがないので404
http://localhost:7410/items/42 → HTTP 200 ← vite preview は index.html を返す
アプリ内のリンクで移動する分には問題なく、再読み込みやURL直打ちのときだけ404になるのが特徴です。本番のWebサーバーでは、存在しないパスを index.html に向ける設定をします。
location / {
try_files $uri $uri/ /index.html;
}
この設定では存在しないURLも200で返るため、404は Vue 側のページで表示します。
ファイルベースルーティングと型付きルート(v5の新しい書き方)
v5 では、src/pages にファイルを置くだけでルートが作られ、ルート名や params に型が付きます。 設定は Vite プラグインを1つ足すだけです。
設定とフォルダ構成
npm i vue-router@5
npm i -D typescript vue-tsc # 型チェックする場合
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueRouter from 'vue-router/vite'
export default defineConfig({
plugins: [
// ファイルベースルーティング(vue() より前に置く)
VueRouter({ dts: 'src/route-map.d.ts' }),
vue(),
],
})
// src/main.ts
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { routes, handleHotUpdate } from 'vue-router/auto-routes'
import App from './App.vue'
const router = createRouter({ history: createWebHistory(), routes })
if (import.meta.hot) handleHotUpdate(router)
createApp(App).use(router).mount('#app')
| ファイル | 生成されるパス | ルート名 |
|---|---|---|
src/pages/index.vue |
/ |
/ |
src/pages/items/[id].vue |
/items/:id |
/items/[id] |
src/pages/users/[userId].vue(親) |
/users/:userId |
/users/[userId] |
src/pages/users/[userId]/tasks.vue(子) |
/users/:userId/tasks |
/users/[userId]/tasks |
src/pages/[...path].vue |
/:path(.*)(404) |
/[...path] |
users/[userId].vue と同名のフォルダ users/[userId]/ を置くと、フォルダ内のファイルが子ルートになり、親の <RouterView /> に表示されます。 既定の読み込みは importMode: 'async' なので、ページごとの遅延読み込みも自動です(ビルドで _id_-*.js・about-*.js などに分かれました)。
<!-- src/pages/items/[id].vue -->
<script setup lang="ts">
import { useRoute } from 'vue-router'
// ルート名を渡すと route.params.id が string として補完される
const route = useRoute('/items/[id]')
</script>
<template><h1 id="title">備品 {{ route.params.id }} の詳細</h1></template>
<!-- src/pages/[...path].vue -->
<script setup lang="ts">
// definePage でルートの meta や name を追加できる(import 不要のマクロ)
definePage({ meta: { title: '404' } })
</script>
<template><h1 id="title">ページが見つかりません</h1></template>
Chromium での確認結果です(vite preview --port 7413)。
初期表示 => 現在のパス: / / ホーム
pages/items/[id].vue => 現在のパス: /items/42 / 備品 42 の詳細
pages/users/[userId]/tasks.vue => 現在のパス: /users/7/tasks / ユーザー 7
child: タスク一覧画面
pages/about.vue => 現在のパス: /about / このアプリについて
pages/[...path].vue => 現在のパス: /nothing/here / ページが見つかりません
型付きルートでタイプミスを見つける
ビルド時に src/route-map.d.ts が生成され、すべてのルート名と params の型(例:'/items/[id]' の params は { id: string })が書き込まれます。
わざと間違えたコードを vue-tsc --noEmit で型チェックしました。
// src/typo.ts
import { useRouter } from 'vue-router'
export function goItem() {
const router = useRouter()
router.push({ name: '/item/[id]', params: { id: '1' } }) // ルート名のタイプミス
router.push({ name: '/items/[id]' }) // params の書き忘れ
router.push({ name: '/items/[id]', params: { itemId: '1' } }) // param 名の間違い
}
src/typo.ts(5,15): error TS2345: ...
Type '"/item/[id]"' is not assignable to type '"/" | "/[...path]" | "/about" | "/items/" | "/items/[id]" | "/users/[userId]" | "/users/[userId]/tasks" | undefined'. Did you mean '"/items/[id]"'?
src/typo.ts(7,48): error TS2345: ...
Object literal may only specify known properties, and 'itemId' does not exist in type '{ id: string | number; }'.
ルート名のタイプミスは「Did you mean '/items/[id]'?」と候補つきで、param名の間違いも型エラーになりました。 一方、params をまるごと書き忘れた6行目は型エラーになりませんでした。 名前付きルートで params を省略すると今のURLの params を引き継ぐ仕様があるため、型の上では省略できる扱いになっています。今のURLに id がない画面から呼ぶと、前の節のとおり実行時に Missing required param "id" になるので、params を持つルートへの遷移は params まで書く習慣をつけると確実です。
※ vue-tsc 3.3.11 は typescript 7.0.2 では ERR_PACKAGE_PATH_NOT_EXPORTED で起動しなかったため、6.0.3 で実行しました。
どちらの書き方を選ぶか
| 観点 | routes 配列 | ファイルベース(v5) |
|---|---|---|
| 設定の見通し | 1ファイルで全体が見える | フォルダ構成がそのままURL |
| ルート名・params の型 | 自分で型を書く | 自動生成 |
| 既存プロジェクト | そのまま | 移行にはファイル配置の変更が必要 |
新規かつTypeScriptならファイルベース、既存プロジェクトは routes 配列のまま v5 に上げるのが負担の少ない選び方です。どちらも同じ createRouter の上で動くので、ガードや RouterLink の書き方は共通です。
よくあるつまずきと直し方
| 症状 | 原因 | 直し方 |
|---|---|---|
No match for {"name":...} |
ルート名の間違い | 名前を確認。型付きルートで事前に検出 |
Missing required param "id" |
params の書き忘れ | params: { id } を渡す |
| 再読み込みで404 | サーバーにフォールバック設定がない | try_files ... /index.html など |
| params が変わっても画面が更新されない | コンポーネントが再利用される | watch(() => route.params.id, ...) か onBeforeRouteUpdate |
| ガードが無限にリダイレクトする | 転送先にも同じ条件がかかる | 転送先(login)を条件から外す |
よくある質問
Q. Vue Router 4 のプロジェクトは v5 に上げるべきですか?
A. routes 配列で書いているなら、公式の移行ガイドで破壊的変更はないとされています。v6 では非推奨APIの削除とESMのみへの移行が予定されているため、v5 で next() を値を返す書き方に直すなど、少しずつ準備しておくと移行が楽になります。
Q. useRoute と useRouter の違いは何ですか?
A. useRoute() は「今のルートの情報」(params・query・fullPath・meta)を読むためのもの、useRouter() は「ルーター本体」で、push などの遷移やガードの登録に使います。
Q. ログイン状態はどこに持てばよいですか?
A. 小さな例では ref をモジュールに置く形で十分ですが、アプリ全体で使う状態は Pinia のストアにまとめると、ガードからもコンポーネントからも同じ値を扱えて管理しやすくなります。ガードの中で useXxxStore() を呼べば使えます。
まとめ
- Vue Router 5.3.1 が最新。routes 配列の書き方は v4 からそのまま動く
- v5 の目玉は本体に統合されたファイルベースルーティング。
vue-router/viteを足すだけで ルート名と params に型が付く - リンクは
RouterLinkを名前 + params で、表示はRouterView、JSからはpush/replace - ガードは値を返して制御(
falseで中止、位置を返して転送)。next()は v5 で非推奨 () => import()でページごとにチャンクが分かれ、開いたときに読み込まれることをビルドとブラウザで確認- 404 は
/:pathMatch(.*)*、履歴モードはサーバーで index.html へのフォールバックを設定
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Vue Routerの使い方|v5対応・ガードと404を実行結果つきで」
株式会社RJCでは、ルーティングの設計や型で間違いを早く見つける工夫を、プロジェクトの中で仲間と試しながら共有しています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。