はじめに
VueでSPAを作成する際によく使われるVue Routerにて、現在のコンポーネントをリロードさせる方法について考えてみました。
クエリやパラメータなどを変更して再読み込みしたい場合など、リロードしたいケースが出てくる時はあると思います。
ただ、同じルートだとrouter.push
を使用してもコンポーネントのリロードは行われないので、方法を考える必要がありますね。
調べてみると、こちらの記事やその他の記事でrouter.go({ path: router.currentRoute.path, force: true })
でできるとありましたが、どこかで仕様が変わったのか現在(ver4.4.0)だと動作しません。
恐らくVue3になり、それに対応するver4.xから色々と変わったのだろうと思いますが…
考えて実装してみました。
方法
ということで早速ですが、実装したのが以下のコードです。
<script setup>
import { RouterView } from 'vue-router';
import useRouterComponentStore from './router-component-store.js';
const routerComponentStore = useRouterComponentStore();
</script>
<template>
<RouterView :key="routerComponentStore.key"/>
</template>
import { defineStore } from 'pinia';
const useRouterComponentStore = defineStore('routerComponent', () => {
const key = ref(1);
/** コンポーネントのリロード */
function reload () {
key.value *= -1;
}
return { key, reload };
});
export default useRouterComponentStore;
import { createApp } from 'vue';
import { createRouter, createWebHashHistory } from 'vue-router';
import AppRouter from './app-router.vue';
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
name: 'home',
component: () => { return import('./views/home.vue'); }
},
{
path: '/detail',
name: 'detail',
component: () => { return import('./views/detail.vue'); }
},
// その他ルーティング設定
]
});
createApp(AppRouter)
.use(router)
.mount('#app');
ひとまず解説をすると、<RouterView :key="routerComponentStore.key"/>
が肝になります。
Vueの特別な属性としてkey
が存在しており、これの値を変動させることでそのコンポーネントを再読み込みさせることができます。
つまりRouterView
にkey
を設定し、リロードしたい時にそのkey
の値を変更すれば現在のルートのコンポーネントをリロードできる訳ですね。
後はkey
の値を変更させる方法になります。
v-model:key
で設定して各router内のコンポーネント側で個々にemitする方法とかもありますが、今回はpiniaを使用しています。
piniaでrouterComponentストアを定義してそこにkey
の値を設定し、key
を更新するreload
関数を定義します。
関数内の処理ではとにかくkey
の値が変わればいいので、何回呼び出しても無限に値が変わるように1と-1を交互に繰り返すロジックにしてみました。
後は個々のコンポーネントでrouterComponentストアを呼び出し、reload
関数を実行すればリロードできます。こんな感じ。
<script setup>
import useRouterComponentStore from './router-component-store.js';
const routerComponentStore = useRouterComponentStore();
</script>
<template>
<div>詳細</div>
<button type="button" @click="routerComponentStore.reload()">画面更新!</button>
</template>
これでリロードをお好みのタイミングでできますね!
注意点
今更ですが、この方法は再マウントが走るので処理としては重い形になります。(よっぽどのことが無い限りは気にするほどではありませんが)
onBeforeRouteUpdate
を使用する形で十分なのか?など、本当に必要かを考えたほうがいいですね。