【Vue.js】ルーティングによる画面切り替え|Vue Routerの基本構成と実装手順

【Vue.js】ルーティングによる画面切り替え|Vue Routerの基本構成と実装手順 Vue.js

Vue Routerは、Vueの公式のルーティングライブラリです。URLに応じて表示するコンポーネントを切り替え、ページ全体を再読み込みせずに、複数の画面を持つ「シングルページアプリケーション(SPA)」を作れます。この記事では、Vue Router 4を前提に、導入から基本の設定、そして最もつまずきやすい「履歴モードとサーバー設定」までを整理します。

スポンサーリンク

導入

新しく始めるなら、Vueの公式ツールcreate-vue(npm create vue@latest)がおすすめです。対話の中で、Vue Routerを含めるかを選べます。既存のプロジェクトに追加する場合は、Vue 3用のVue Router 4をインストールします。

Shell:Vue Routerの導入
# 新しく始めるなら:対話で「Vue Router」を選ぶと、最初から組み込まれる
npm create vue@latest

# 既存のプロジェクトに追加する場合(Vue 3 用は Vue Router 4)
npm install vue-router@4

基本の設定

必要なのは、3つのファイルです。ルーターの定義、アプリへの組み込み、そしてルートのコンポーネントを表示する場所の用意です。

ルーターの定義

URLのパスと、表示するコンポーネントの対応を、routesの配列に書きます。createRouterのhistoryには、本番のアプリでは、createWebHistory()(履歴モード)か、createWebHashHistory()(ハッシュモード)を指定します。違いは、後の節で説明します。

JavaScript:src/router/index.js
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'

const routes = [
  { path: '/', name: 'Home', component: Home },
  { path: '/about', name: 'About', component: About }
]

const router = createRouter({
  // HTML5 の履歴モード。きれいな URL(/about)になる
  // import.meta.env.BASE_URL は、サブフォルダに置くときの基準のパス(create-vue の既定の書き方)
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

export default router

アプリへの組み込み

Vueのプラグインと同じく、use()はmount()より前に呼びます。順序を逆にすると動きません。

JavaScript:src/main.js
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

// use() は、mount() より前に呼ぶ
createApp(App).use(router).mount('#app')

RouterLinkとRouterView

<RouterView>は、現在のルートのコンポーネントを描画する場所です。リンクには、通常の<a>ではなく<RouterLink>を使います。公式が説明しているとおり、RouterLinkなら、ページを再読み込みせずにURLを変えられます。現在のページへのリンクには、router-link-exact-activeなどのクラスが自動で付くので、スタイルで強調できます。

Vue:src/App.vue
<!-- src/App.vue -->
<template>
  <nav>
    <!-- <a> の代わりに RouterLink を使う:ページを再読み込みせずに、URL だけが変わる -->
    <RouterLink to="/">Home</RouterLink>
    <RouterLink :to="{ name: 'About' }">About</RouterLink>
  </nav>

  <!-- 現在のルートのコンポーネントが、ここに表示される -->
  <RouterView />
</template>

<style scoped>
/* 現在のページのリンクには、自動でクラスが付く */
nav a.router-link-exact-active {
  font-weight: bold;
}
</style>

パラメータの取得と、プログラムからの遷移

<script setup>では、useRoute()で現在のルート(パラメータやクエリ)を読み、useRouter()でルーター本体を取得して遷移させます。/user/:idのように、コロンで始まる部分が、URLによって変わるパラメータです。

Vue:useRouteとuseRouter
<!-- src/views/UserDetail.vue -->
<script setup>
import { useRoute, useRouter } from 'vue-router'

const route = useRoute()      // 現在のルート(パラメータやクエリを読む)
const router = useRouter()    // ルーター本体(遷移させる)

// ルートの定義:{ path: '/user/:id', component: UserDetail }
// /user/123 を開いたとき、route.params.id は '123'(文字列)

function goHome() {
  router.push('/')            // プログラムから遷移する
}
</script>

<template>
  <h1>ユーザー {{ route.params.id }}</h1>
  <button type="button" @click="goHome">トップへ戻る</button>
</template>

ここでroute.params.idは、文字列で渡されます。また、/user/1から/user/2へ移動するときは、同じコンポーネントが再利用されるため、onMountedは再実行されません。詳しくはVue Routerで多階層のルート定義を整理する方法をご覧ください。

履歴モードで、直接アクセスが404になる理由

最もつまずきやすいのが、これです。開発中は動いたのに、本番のサーバーに置くと、トップページ以外のURL(/aboutなど)を直接開いたり、リロードしたりしたときに、404になることがあります。

原因は、履歴モードの仕組みです。createWebHistory()は、URLをきれいな形(/about)にしますが、サーバーから見ると、/aboutという名前のファイルは存在しません。公式の説明どおり、サーバーが、静的ファイル以外のすべてのパスにindex.htmlを返すように設定する必要があります。

nginxの場合

nginx:設定ファイル
location / {
  # ファイルやディレクトリが無ければ、index.html を返す
  try_files $uri $uri/ /index.html;
}

Apacheの場合(.htaccess)

レンタルサーバーなどで使われるApacheでは、mod_rewriteで、実在しないパスをindex.htmlに渡します。

Apache:.htaccess
# .htaccess(Apache の場合)
<IfModule mod_negotiation.c>
  Options -MultiViews
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  # 実在するファイルやディレクトリでなければ、index.html に渡す
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

これらの例は、サイトのルートに置く前提です。サブフォルダに置く場合は、パスの基準を調整する必要があります。ルーター側はcreateWebHistory('/sub/')のように指定します(create-vueの既定のimport.meta.env.BASE_URLは、Viteのbase設定を反映します)。サーバー側も、書き換え先を合わせてください。

ハッシュモードという選択肢

サーバーの設定を変えられない場合は、createWebHashHistory()を使う方法があります。URLの#より後ろは、サーバーに送られないため、設定が不要です。ただし、公式も、SEOに悪い影響があると説明しています。検索から流入を得たいサイトでは、履歴モードをおすすめします。

存在しないURLは、404ページを用意する

履歴モードでは、サーバーが常にindex.htmlを返すため、存在しないURLでも、本物の404のステータスは返りません。アプリの側で、どのルートにも一致しなかったURLを受け止める、404用のルートを最後に置きます。

JavaScript:404ページのルート
// routes の最後に置く:どのルートにも一致しなかった URL を受け止める
{
  path: '/:pathMatch(.*)*',
  name: 'NotFound',
  component: () => import('../views/NotFound.vue')
}

次のステップ

基本が分かったら、目的に応じて、次の機能に進んでください。

よくある質問(FAQ)

Q本番で、リロードすると404になります。
A履歴モードでは、サーバーが、静的ファイル以外のすべてのパスにindex.htmlを返す設定が必要です。nginxならtry_files、Apacheなら.htaccessのmod_rewriteで設定してください。設定できない場合は、ハッシュモードも選べます。
QRouterLinkと<a>タグは、何が違いますか?
ARouterLinkは、ページを再読み込みせずに、URLを変えて画面を切り替えます。通常の<a>だと、ページ全体が再読み込みされ、SPAの利点が失われます。外部サイトへのリンクには、通常の<a>を使います。
Q履歴モードとハッシュモードは、どちらを選ぶべきですか?
Aサーバーを設定できるなら、きれいなURLの履歴モードがおすすめです。設定できない場合は、ハッシュモードで動かせますが、公式もSEOへの悪影響を指摘しています。
Quse(router)をmountの後に書いたら、動きません。
AVueのプラグインは、mount()より前にuse()で登録する必要があります。createApp(App).use(router).mount('#app')の順に書いてください。

まとめ

Vue Routerは、ルートの定義、use(router)、<RouterLink>と<RouterView>の3点で、基本の画面切り替えが作れます。本番に置くときは、履歴モードに対応したサーバーの設定と、404ページの用意を忘れないようにしてください。