Route系の動きを制御する際に、Vue3のComposition APIでは以下のようなやり方をとっています。
しかし、これが使えるのはVue Routerの4.x系以降になります。プロジェクトによっては事情があって3系を使い続けているという場合もあるかと思いますので対処法をまとめました。
また、今後4.x系に移行するぜ!という方にもおすすめできるようにimport
のfrom
を以降を書き換えるだけで移行が完了するようにしております。
import { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
function pushWithQuery(query) {
router.push({
name: 'search',
query: {
...route.query,
},
})
}
},
}
対処したいこと
対処したい事柄は基本的に以下二つになるのかなと思います。
両方ともvue-router@3系のままではストレートにsetup()な書き方はできません。
-
useRouter()
,useRoute()
が使いたい - ナビゲーションガードはどうすんねん
一つ一つ解消していきます。
1. useRouter()
,useRoute()
が使いたい
useRouter
とuseRoute
をexportするプラグインを作成しましょう。
plugins
とかcomposable
の中にいれて利用しましょう。
import {
ComponentInternalInstance,
getCurrentInstance,
} from "@vue/composition-api";
import VueRouter, { NavigationGuard, Route } from "vue-router";
/**
* 現在のインスタンスを取得する
* setup関数内で呼ばれているかの確認のためにこのメソッドをかませる
* @returns インスタンス
*/
function getInstance(): ComponentInternalInstance {
const instance = getCurrentInstance();
if (!instance) {
throw new Error(`Should be used in setup()`);
}
return instance;
}
/**
* 4.x系で使えるuseRouterの代替メソッド
* setup()関数内でのみ使用可能
* @returns Routerオブジェクト
*/
export function useRouter(): VueRouter {
// インスタンスにアクセス
const instance = getInstance();
// proxyが従来の`this`にあたるコンポーネントインスタンス
return instance.proxy.$router;
}
/**
* 4.x系で使えるuseRouteの代替メソッド
* setup()関数内のみで使用可能
* @returns Routeオブジェクト
*/
export function useRoute(): Route {
// インスタンスにアクセス
const instance = getInstance();
// proxyが従来の`this`にあたるコンポーネントインスタンス
return instance.proxy.$route;
}
使うとき
置き場所からimport
したらvue-router@4系での利用方法と全く一緒です。
後々移行しようかな〜と考えているのならばfrom以降を変えるだけなのでラクチン
<script lang="ts">
import { defineComponent } from "@vue/composition-api";
import { useRouter, useRoute } from "@/plugins/router.ts"
export default defineComponent({
name: "xxx",
setup() {
const router = useRouter()
const route = useRoute()
// この下で好きなように利用
},
});
</script>
ナビゲーションガードはどうすんねん
onBefoureUpdate
などのナビゲーションガードをsetup()内で利用するのはどうするのか。
同じように、てかさっきと同じファイルに書いてあげます
getCurrentInstance()
部分はさっき作成したgetInstance()
に置き換えるのがいいと思います。
import { getCurrentInstance } from "@vue/composition-api";
import { NavigationGuard } from "vue-router";
import Vue from "vue";
import { ComponentOptions } from "vue/types/umd";
/**
* ナビゲーションガード系メソッドを生やす為のメソッド
* @param name ナビゲーションガード名
* @param callback コールバック
*/
function onHook(
name: keyof ComponentOptions<Vue>,
callback: NavigationGuard<Vue>
): void {
const vm = getCurrentInstance();
const merge = Vue.config.optionMergeStrategies[name];
if (vm && merge) {
const prototype = Object.getPrototypeOf(vm.proxy.$options);
prototype[name] = merge(vm.proxy.$options[name], callback);
}
}
/**
* 4.x系で使えるonBeforeRouteUpdateの代替メソッド
* @param callback ナビゲーションガードコールバック関数
*/
export function onBeforeRouteUpdate(callback: NavigationGuard<Vue>): void {
return onHook("beforeRouteUpdate", callback);
}
/**
* 4.x系で使えるonBeforeRouteLeaveの代替メソッド
* @param callback ナビゲーションガードコールバック関数
*/
export function onBeforeRouteLeave(callback: NavigationGuard<Vue>): void {
return onHook("beforeRouteLeave", callback);
}
使い方
使い方は4.x系の公式と同じなのでそちらをみてください
4.x系に移行するには
vue-routerの4系をいれて、
> npm i vue-router@4
from
以降を置き換えるだけ
ナビゲーションガードも同じ
<script lang="ts">
import { defineComponent } from "@vue/composition-api";
// import { useRouter, useRoute } from "@/plugins/router.ts"
// ↓に置き換え
import { useRouter, useRoute } from "vue-router"
export default defineComponent({
name: "xxx",
setup() {
const router = useRouter()
const route = useRoute()
// この下で好きなように利用
},
});
</script>