Vue.js でファイルベースルーティングを実現する「Unplugin Vue Router」の紹介
Web アプリケーション開発において、ファイルベースルーティングはコードの可読性向上や開発効率化に大きく貢献します。本記事では、Vue.js アプリケーションでのファイルベースルーティングをシンプルかつ型安全に実現できる「Unplugin Vue Router」をご紹介します。Nuxt のような大規模なフレームワークは少し大げさだと感じる場合や、Vue Router をより効率的に活用したい方はぜひ参考にしてください。
Unplugin Vue Router 概要
- ファイルベースルーティングによってファイルのパスと 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
でプラグインを読み込みます。
+ import VueRouter from 'unplugin-vue-router/vite'
export default defineConfig({
plugins: [
+ VueRouter(), // vue より前に書く
vue()
],
次に tsconfig.app.json
に型定義ファイルを追加します。この型定義ファイルは開発サーバー実行中やビルド時に生成・更新されます。
(ドキュメントには "moduleResolution": "Bundler"
の記載もありますが、create-vue で生成した場合は tsconfig.node.json
に設定されています)
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": [
"env.d.ts",
"src/**/*",
"src/**/*.vue",
+ "./typed-router.d.ts"
],
"exclude": ["src/**/__tests__/*"],
また、env.d.ts
にはエディター向けの型定義ファイルを追加します。
/// <reference types="vite/client" />
+/// <reference types="unplugin-vue-router/client" />
これで準備は完了です。
使い方
ページの作成
ここからいよいよルーティングについて解説していきます。
まず、main.ts
で Vue Router を読み込みます。
+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
ディレクトリにページのファイルを作ります。
<template>
<h1>Home</h1>
</template>
<template>
<h1>About</h1>
<p>Hello, world!</p>
</template>
pages/index.vue
は /
の URL で表示される内容で、
pages/about.vue
は /about
の内容になります(/about/index.vue
でもよい。Nuxt と同じ)。
そして App.vue
を以下のように変更します。
<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
タグ部分には赤い枠を追加してあります)
Go to About
のリンクを押すと、アドレスバーが http://localhost:5173/about に変わり、main
タグ内が about.vue
の内容に置き換わることが分かります。
動的なルーティング
動的なルーティングも簡単に設定できます。
ファイル名を角括弧で囲むことによりパスのパラメータを表現します。つまり、src/pages/users/[id].vue
というファイルを作ると、/users/(任意の文字列)
の URL でアクセスできるようになります(これも Nuxt と同じですね)。
まずは今まで同じように静的なページを作ります。
<template>
<h1>User</h1>
</template>
<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>
この状態だと、どちらのリンクも同じタイトルが表示されるだけです。
ページ内でパラメータを使用するには useRoute
コンポーザブルを使用します。
+<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
ファイルを作成した時点で型定義ファイルが更新されているため、入力補完が表示されます。
これで各ページごとに固有の内容を表示できるようになりました。
データローダー
Unplugin Vue Router はデータローダー機能も備えています。
かなり多機能なのでさわりだけ紹介すると、ルートに基づくデータフェッチまわりの処理を簡潔に記述できます。
<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
Discussion