Laravel9/Vue3でVue-Router4

Vue-Routerもv4へバージョンアップしたようなので、v3とv4の書き方の比較を交えながら、Laravel9/Vue3環境でVue-Router4のインストール&設定、useRoute、useRouter、scrollBehaviorの使い方について確認する。

開発環境

Windows10
Docker Desktop
WSL2
Laravel9
Laravel Sail
Vue3.2.40

Vue-Router4のインストール&設定

Vue-Router4インストール

$ sail npm install vue-router@4

router.js

import { createRouter, createWebHistory } from 'vue-router'
const BASE_URL = '/app'

import Home from './components/Home.vue'
import About from './components/About.vue'
import Item from './components/Item.vue'
import NotFound from './components/404.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
  },
  {
    path: '/about',
    name: 'About',
    component: About,
  },
  {
    path: '/item/:id(\\d+)',
    name: 'Item',
    component: Item,
  },
  {
    path: '/404',
    name: '404-NotFound',
    component: NotFound,
  },
  {
    path: '/:pathMatch(.*)',
    redirect: '/404',
  },
]

const router = createRouter({
  history: createWebHistory(BASE_URL),  // set BASE_URL
  routes
})

export default router

すべてをキャッチするにはv3では「path: ‘/‘」だったがv4では「path: ‘/:pathMatch(.)’」

path: '/*',
redirect: '/404',
↓
path: '/:pathMatch(.*)',
redirect: '/404',

app.js

import './bootstrap';
import router from './router'			// 追記
import { createApp } from 'vue'
import App from './components/App.vue'

const app = createApp(App)
app.use(router)							// 追記
app.mount("#app")

App.vue

      <main>
        <router-view></router-view>
      </main>

現在のルート情報の取得(useRoute)

v3ではthis.$routeで取得できたがv4ではuseRoute関数を使用してpath、name、params、query、hashなど現在のルート情報を取得。

<template>
  <h1>useRoute</h1>
  <p>path: {{ route.path }}</p>
  <p>name: {{ route.name }}</p>
  <p>params: {{ route.params }}</p>
  <p>query: {{ route.query }}</p>
  <p>hash: {{ route.hash }}</p>
  <p>{{ message }}</p>
</template>

<script setup>
  import { ref, onMounted, watch } from 'vue';
  import { useRoute } from 'vue-router'
  const route = useRoute()
</script>

同一コンポーネントへの遷移

同一コンポーネントへの遷移において、onMounted()などは一度しか呼び出されないので、routeを監視して変化があれば更新してやる必要がある。

<script setup>
  import { reactive, onMounted, watch } from 'vue';
  import { useRoute } from 'vue-router'
  const route = useRoute()
  const item = reactive(null)
  onMounted(() => {
    item = getItem()
  })
  const getItem = () => {
    axios.get…
  }
  watch(route, () => {
    item = getItem()
  })
</script>

遷移前のRoute情報取得

computed()で取得したRoute名をwatch()で監視

<script setup>
  import { ref, computed, watch } from 'vue'
  import { useRoute } from 'vue-router'

  const route = useRoute()

  // 遷移前のRoute名
  const prevRouteName = ref('')
  // Route名の取得
  const routeName = computed(() => {
    return route.name
  })
  // Route履歴の監視
  watch(routeName, (now, prev) => {
    prevRouteName.value = prev
  })
</script>

VueRouterのインスタンスを取得(useRouter)

v3ではthis.$routerで取得できたが、v4ではuseRouter関数を使用。

例)プログラムによる遷移

import { useRouter } from 'vue-router'
const router = useRouter()

const toItemPage = () => {
  router.push({
    path: '/item',
    name: 'Item',
    params: { id: 123 },
    query: { type: 'sample' },
    hash: '#abc'
  })
}

/item/123?type=sample#abc

スクロール位置制御(scrollBehavior)

・同一コンポーネントへの遷移時には常にページトップにスクロールしたい。
・hashが設定されたリンクへの遷移ではhash位置(アンカーリンク)までスクロールしなければならない。
このようなスクロール位置制御はscrollBehavior関数を利用する。

const router = createRouter({
  history: createWebHistory(BASE_URL),  // set BASE_URL
  routes,
  scrollBehavior(to, from, savedPosition) {
    if (to.hash) {
      return {
        el: to.hash,
        behavior: 'smooth'		// スムーズにスクロール
      }
    } else 
    if (savedPosition) {
      return savedPosition
    } else {
      return {
        top: 0
      }
    }
  },
})

スクロール位置の設定

v3では縦横軸がx、yだったのに対してv4ではtop、leftに変更されている模様。

return { x: 0, y: 0 }
↓
return { top: 0, left: 0 }

ページ内リンク(アンカーリンク)へのスクロールではヘッダー部等のオフセットが無視されるので、別途設定が必要。

    if (to.hash) {
      return {
        el: to.hash,
        top: 60,			// 追記
        behavior: 'smooth'
      }
    }

※ページ内アンカーリンクでリロード時にヘッダー部等のオフセットが復元されない問題があった。

scrollBehaviorとvue-scrolltoとの併用

リロード時のオフセット復元の問題もいずれ修正されると思うが、それまではvue-scrolltoを使用する。

vue-scrolltoのインストール

$ sail npm install vue-scrollto

app.js

import VueScrollTo from 'vue-scrollto'

const app = createApp(App)
app.use(VueScrollTo, {
    offset: -60,	// オフセットも設定できる
})
app.mount("#app")

router.js

import VueScrollTo from 'vue-scrollto'
…
const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from, savedPosition) {
    if (to.hash) {
      VueScrollTo.scrollTo(to.hash)
      return {}
    } else if (savedPosition) {
       return savedPosition
    } else {
      VueScrollTo.scrollTo('#app')
      return {}
    }
  },
})

export default router