Vue Routerの基本概念と実装方法

Vue Routerはシングルページアプリケーション(SPA)におけるコンポーネント間のナビゲーションを管理するためのVue.js公式ルーティングライブラリです。本記事ではVue Routerの基本的な使い方と主要機能について解説します。

Vue Routerとは

Vue Routerは、従来のマルチページアプリケーションで使用されるサーバーサイドルーティングとは異なり、クライアントサイドでのルーティングを提供します。これによりページ全体のリロードなしでコンテンツを切り替えることが可能になります。

基本的なセットアップ

VueプロジェクトでVue Routerを使用する基本的なセットアップ方法を見てみましょう。

HTML構造

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Vue Router Example</title>
</head>
<body>
  <div id="app">
    <h1>ルーティングデモ</h1>
    <nav>
      <!-- router-linkコンポーネントでナビゲーションリンクを作成 -->
      <router-link to="/home">ホーム</router-link>
      <router-link to="/about">关于我们</router-link>
      <router-link to="/contact">お問い合わせ</router-link>
    </nav>
    <!-- ルートにマッチしたコンポーネントがここに描画されます -->
    <router-view></router-view>
  </div>

  <script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script>
  <script src="https://unpkg.com/vue-router@3.5.3/dist/vue-router.js"></script>
</body>
</html>

JavaScript実装

// ルーティングに必要なVueとVueRouterをインポート
const { Vue, VueRouter } = window;

// 1. ルーティング対象のコンポーネントを定義
const HomeView = {
  template: '<div><h2>ホームページ</h2><p>ようこそ、私たちのサイトへ!</p></div>'
};

const AboutView = {
  template: '<div><h2>关于我们</h2><p>私たちについての詳細情報です。</p></div>'
};

const ContactView = {
  template: '<div><h2>お問い合わせ</h2><p>ご連絡はこちらからお願いします。</p></div>'
};

// 2. ルート定義
const routes = [
  { path: '/home', component: HomeView },
  { path: '/about', component: AboutView },
  { path: '/contact', component: ContactView }
];

// 3. VueRouterインスタンスを作成
const router = new VueRouter({
  routes
});

// 4. ルートを含むVueインスタンスをマウント
const app = new Vue({
  router
}).$mount('#app');

動的ルートパラメータ

動的ルートパラメータを使用すると、URLの一部を変数として扱うことができます。

const UserView = {
  template: '<div><h2>ユーザー情報</h2><p>ID: {{ userId }}</p></div>',
  computed: {
    userId() {
      return this.$route.params.id;
    }
  }
};

const dynamicRoutes = [
  { 
    path: '/user/:id', 
    component: UserView,
    props: true // propsでルートパラメータをコンポーネントに渡す
  }
];

const dynamicRouter = new VueRouter({
  routes: dynamicRoutes
});

ルートパラメータの変更への対応

ルートパラメータが変更された場合、コンポーネントインスタンスは再利用されます。この変化に対応するには、watchプロパティを使用します。

const ProfileView = {
  template: '<div><h2>プロフィール</h2><p>ユーザーID: {{ userId }}</p></div>',
  watch: {
    '$route'(to, from) {
      // ルート変更時の処理
      console.log('ルートが変更されました:', to.params.id);
      this.fetchUserData(to.params.id);
    }
  },
  methods: {
    fetchUserData(userId) {
      // ユーザーデータを取得する処理
      console.log(`ユーザーID ${userId} のデータを取得中...`);
    }
  }
};

高度なマッチングパターン

Vue Routerはpath-to-regexpを使用しているため、様々な高度なマッチングパターンをサポートしています。

const advancedRoutes = [
  // オプションのパラメータ
  { path: '/products/:category?', component: CategoryView },
  
  // 複数のパラメータ
  { path: '/blog/:year/:month/:day', component: BlogView },
  
  // 正規表現によるマッチング
  { path: '/search/:query(\\d+)', component: SearchView }
];

ルートの優先順位

複数のルートが同じパスにマッチする場合、定義された順序が優先されます。

const priorityRoutes = [
  // このルートが優先される
  { path: '/user/:id', component: UserProfile },
  
  // このルートは優先されない
  { path: '/user/admin', component: AdminView }
];

プログラムによるナビゲーション

router-link以外にも、JavaScriptコードから直接ナビゲーションを行うことができます。

// router.push() - historyスタックに新しいエントリを追加
router.push('/home');
router.push({ name: 'user', params: { id: 123 } });

// router.replace() - 現在のhistoryエントリを置き換え
router.replace('/about');

// router.go() - historyスタック内を移動
router.go(1);  // 前進
router.go(-1); // 戻る

名前付きルート

ルートに名前を付けることで、ナビゲーションをより直感的に行えます。

const namedRoutes = [
  {
    path: '/product/:id',
    name: 'productDetail',
    component: ProductDetail
  }
];

// 名前付きルートを使用したナビゲーション
router.push({ name: 'productDetail', params: { id: 456 } });

名前付きビュー

複数のビューを同時に表示する場合、名前付きビューを使用します。

<router-view name="header"></router-view>
<router-view name="main"></router-view>
<router-view name="footer"></router-view>
const namedViews = [
  {
    path: '/',
    components: {
      default: MainContent,
      header: HeaderView,
      footer: FooterView
    }
  }
];

リダイレクトとエイリアス

リダイレクト

特定のパスへのアクセスを別のパスにリダイレクトできます。

const redirectRoutes = [
  { path: '/old-path', redirect: '/new-path' },
  { path: '/user', redirect: { name: 'userProfile' } }
];

エイリアス

URLを保持したまま、異なるパスにマッチさせる機能です。

const aliasRoutes = [
  { 
    path: '/dashboard', 
    component: Dashboard,
    alias: '/home' 
  }
];

HTML5 Historyモード

hashモードではなく、よりクリーンなURLを使用するための設定です。

const historyRouter = new VueRouter({
  mode: 'history',
  routes: [...]
});

ナビゲーションガード

ルート遷移を制御するためのフック機能です。

グローバルガード

router.beforeEach((to, from, next) => {
  // 認証チェックなどの前処理
  if (to.meta.requiresAuth && !isAuthenticated()) {
    next('/login');
  } else {
    next();
  }
});

ルート固有のガード

const guardedRoutes = [
  {
    path: '/admin',
    component: AdminPanel,
    beforeEnter: (to, from, next) => {
      // このルート固有のガード
      if (!isAdmin()) {
        next('/access-denied');
      } else {
        next();
      }
    }
  }
];

コンポーネント内ガード

const ProtectedComponent = {
  template: '<div>保護されたコンテンツ</div>',
  beforeRouteEnter(to, from, next) {
    // コンポーネントが作成される前に呼ばれる
    checkPermission().then(() => {
      next();
    }).catch(() => {
      next(false);
    });
  },
  beforeRouteLeave(to, from, next) {
    // コンポーネントが離脱する前に呼ばれる
    if (confirm('本当に離脱しますか?')) {
      next();
    } else {
      next(false);
    }
  }
};

データ取得

ルート遷移時にデータを取得する方法についてです。

ナビゲーション完了後のデータ取得

const PostView = {
  template: `
    <div>
      <div v-if="loading">読み込み中...</div>
      <div v-if="error">{{ error }}</div>
      <div v-if="post">
        <h2>{{ post.title }}</h2>
        <p>{{ post.content }}</p>
      </div>
    </div>
  `,
  data() {
    return {
      post: null,
      loading: false,
      error: null
    };
  },
  created() {
    this.fetchPost();
  },
  watch: {
    '$route': 'fetchPost'
  },
  methods: {
    async fetchPost() {
      this.loading = true;
      this.error = null;
      try {
        const response = await fetch(`/api/posts/${this.$route.params.id}`);
        this.post = await response.json();
      } catch (err) {
        this.error = 'データの取得に失敗しました';
      } finally {
        this.loading = false;
      }
    }
  }
};

ナビゲーション完了前のデータ取得

const AsyncPostView = {
  template: `
    <div>
      <div v-if="loading">読み込み中...</div>
      <div v-if="error">{{ error }}</div>
      <div v-if="post">
        <h2>{{ post.title }}</h2>
        <p>{{ post.content }}</p>
      </div>
    </div>
  `,
  data() {
    return {
      post: null,
      error: null
    };
  },
  beforeRouteEnter(to, from, next) {
    fetchPostData(to.params.id)
      .then(post => {
        next(vm => {
          vm.post = post;
        });
      })
      .catch(err => {
        next(false);
      });
  },
  watch: {
    '$route'() {
      this.post = null;
      this.fetchPost();
    }
  },
  methods: {
    async fetchPost() {
      try {
        const response = await fetch(`/api/posts/${this.$route.params.id}`);
        this.post = await response.json();
      } catch (err) {
        this.error = 'データの取得に失敗しました';
      }
    }
  }
};

これらの機能を組み合わせることで、複雑なナビゲーションロジックを持つシングルページアプリケーションを構築することができます。Vue Routerの詳細なドキュメントは公式サイトを参照してください。

タグ: vue.js Vue Router SPA フロントエンド開発 javascript

7月27日 20:18 投稿