🗂️

Vue.js でファイルベースルーティングを実現する「Unplugin Vue Router」の紹介

2024/08/13に公開

Web アプリケーション開発において、ファイルベースルーティングはコードの可読性向上や開発効率化に大きく貢献します。本記事では、Vue.js アプリケーションでのファイルベースルーティングをシンプルかつ型安全に実現できる「Unplugin Vue Router」をご紹介します。Nuxt のような大規模なフレームワークは少し大げさだと感じる場合や、Vue Router をより効率的に活用したい方はぜひ参考にしてください。

Unplugin Vue Router 概要

https://uvr.esm.is/

  • ファイルベースルーティングによってファイルのパスと URL を直接対応させることで、コードの構造を直感的に理解できます。
  • TypeScript との連携によりコンパイル時にルーティングのミスを検出でき、より安全な開発が可能になります。
  • Nuxt のようなフルスタックフレームワークと比較して軽量であり、小規模なプロジェクトに最適です。
  • 実は Nuxt 内部でも Unplugin Vue Router が利用されています。
  • 名前にあるように unjs/unplugin というプラグインシステムをベースに作られています

セットアップ

プロジェクトの作成

まずは Vue.js のプロジェクトを作成しましょう。create-vue を実行し、オプションを選択していきます。
TypeScript は Yes を、Vue Router はNo を選択します(残りはお好みで)。ここで Vue Router を入れてしまうと余計もファイルが生成されてしまうので後で入れます。

$ npm create vue@latest

オプション選択

ライブラリのインストール

Vue Router と Unplugin Vue Router をインストールします。

$ npm install vue-router
$ npm install -D unplugin-vue-router

各種設定

ドキュメントに沿って各設定を追加していきます。
(差分を分かりやすくするために整形したコードを掲載しています)

まずは vite.config.ts でプラグインを読み込みます。

vite.config.ts
+ import VueRouter from 'unplugin-vue-router/vite'

 export default defineConfig({
   plugins: [
+    VueRouter(), // vue より前に書く
     vue()
   ],

次に tsconfig.app.json に型定義ファイルを追加します。この型定義ファイルは開発サーバー実行中やビルド時に生成・更新されます。
(ドキュメントには "moduleResolution": "Bundler" の記載もありますが、create-vue で生成した場合は tsconfig.node.json に設定されています)

tsconfig.app.json
{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": [
    "env.d.ts",
    "src/**/*",
    "src/**/*.vue",
+   "./typed-router.d.ts"
  ],
  "exclude": ["src/**/__tests__/*"],

また、env.d.ts にはエディター向けの型定義ファイルを追加します。

env.d.ts
 /// <reference types="vite/client" />
+/// <reference types="unplugin-vue-router/client" />

これで準備は完了です。

使い方

ページの作成

ここからいよいよルーティングについて解説していきます。
まず、main.ts で Vue Router を読み込みます。

src/main.ts
+import { createRouter, createWebHistory } from 'vue-router'
+import { routes } from 'vue-router/auto-routes'

+const router = createRouter({
+  history: createWebHistory(),
+  routes
+})

 createApp(App)
+  .use(router)
   .mount('#app')

次に pages ディレクトリにページのファイルを作ります。

src/pages/index.vue
<template>
  <h1>Home</h1>
</template>
src/pages/about.vue
<template>
  <h1>About</h1>
  <p>Hello, world!</p>
</template>

pages/index.vue/ の URL で表示される内容で、
pages/about.vue/about の内容になります(/about/index.vue でもよい。Nuxt と同じ)。

そして App.vue を以下のように変更します。

src
 <script setup lang="ts">
-import HelloWorld from './components/HelloWorld.vue'
-import TheWelcome from './components/TheWelcome.vue'
+import { RouterView, RouterLink } from 'vue-router'
 </script>

...中略...

     <div class="wrapper">
-      <HelloWorld msg="You did it!" />
+      <RouterLink to="/">Go to Home</RouterLink>
+      <RouterLink to="/about">/Go to About</RouterLink>
     </div>
   </header>

   <main>
-    <TheWelcome />
+    <RouterView />
   </main>

上記は(素の)Vue Router の機能です。<RouterLink /> はその名の通りリンクコンポーネントです。<RouterView /> でルート(route の方)コンポーネントのレンダリング場所を指定しています。

ここで開発サーバーを立ち上げてみましょう。

$ npm run dev

http://localhost:5173 にアクセスすると、以下のようなページが表示されます。
(スクリーンショットの main タグ部分には赤い枠を追加してあります)

Home

Go to About のリンクを押すと、アドレスバーが http://localhost:5173/about に変わり、main タグ内が about.vue の内容に置き換わることが分かります。

About

動的なルーティング

動的なルーティングも簡単に設定できます。
ファイル名を角括弧で囲むことによりパスのパラメータを表現します。つまり、src/pages/users/[id].vue というファイルを作ると、/users/(任意の文字列) の URL でアクセスできるようになります(これも Nuxt と同じですね)。

まずは今まで同じように静的なページを作ります。

src/users/[id].vue
<template>
  <h1>User</h1>
</template>
src/App.vue
    <div class="wrapper">
      <RouterLink to="/">Go to Home</RouterLink>
      <RouterLink to="/about">Go to About</RouterLink>
+     <RouterLink to="/users/1">user #1</RouterLink>
+     <RouterLink to="/users/2">user #2</RouterLink>
    </div>

この状態だと、どちらのリンクも同じタイトルが表示されるだけです。

User

ページ内でパラメータを使用するには useRoute コンポーザブルを使用します。

src/users/[id].vue
+<script setup lang="ts">
+import { useRoute } from 'vue-router'
+
+const route = useRoute('/users/[id]')
+</script>

 <template>
   <h1>User</h1>
+  <p>id: {{ route.params.id }}</p>
 </template>

useRoute に引数を渡すことで route.params に正しく型がつきます。
ちなみに開発サーバーを立ち上げた状態で作業している場合、[id].vue ファイルを作成した時点で型定義ファイルが更新されているため、入力補完が表示されます。

useRoute

これで各ページごとに固有の内容を表示できるようになりました。
User2

データローダー

Unplugin Vue Router はデータローダー機能も備えています。
かなり多機能なのでさわりだけ紹介すると、ルートに基づくデータフェッチまわりの処理を簡潔に記述できます。

src/pages/posts/[id].vue
<script lang="ts">
import { defineBasicLoader } from "unplugin-vue-router/data-loaders/basic";

export const usePostData = defineBasicLoader("/posts/[id]", async (route) => {
  const url = `https://jsonplaceholder.typicode.com/posts/${route.params.id}`;
  return fetch(url).then((res) => res.json());
});
</script>

<script setup lang="ts">
const { data, isLoading, error, reload } = usePostData();
</script>

<template>
  <div v-if="isLoading">読込中</div>
  <template v-else>
    <h1>{{ data.title }}</h1>
    <div>{{ data.body }}</div>
  </template>
</template>

おわりに

Unplugin Vue Router は、最小限の設定でファイルベースルーティング + α を簡単に導入できます。リポジトリの README には "still experimental" という記述があるので将来的な API 変更の可能性を考慮しておくとよいでしょう。「Nuxt を使うほどの要件ではないがファイルベースルーティングは欲しいな」という場合にぜひ試してみてください。

参考: 本記事のサンプルリポジトリ https://github.com/jay-es/unplugin-vue-router-example

Vue・Nuxt 情報が集まる広場 / Plaza for Vue・Nuxt.

Discussion