Некорректные данные useRoute
Так уж вышло, что на одном из проектов (Nuxt 3) мне понадобилось
получить meta-данные текущего роута внутри дефолтного шаблона,
чтобы потом завязать на них логику запросов внутри хука onMounted.
Недолго думая, я пошел в default.vue и написал там примерно такое:
import { useRoute, onMounted } from '#imports'
const { meta } = useRoute()
onMounted(() => {
if (meta.someMeta) {
// Тут что-то делаем
}
})
Это решение работало корректно до тех пор, пока в проекте
не появился второй шаблон payment.vue.
Проблема
С появлением второго шаблона (для страницы оплаты) возник так
называемый layout transition (смена макета) при переходе
на страницу оплаты и обратно.
Как выяснилось позже, в таком случае useRoute из Nuxt,
используемый внутри шаблона, ведет себя иначе, чем ожидалось.
Например:
// pages/index.vue
<template>
<div>Главная страница</div>
<NuxtLink to="/cabinet">Перейти в Личный кабинет</NuxtLink>
</template>
// pages/cabinet.vue
<template>
<div>Личный кабинет</div>
<NuxtLink to="/">Перейти на Главную</NuxtLink>
</template>
<script setup>
definePageMeta({
layout: 'cabinet',
someMeta: true
})
</script>
// layouts/default.vue
<template>
<div class="default-layout">
<div>DefaultLayout</div>
<slot />
</div>
</template>
// layouts/cabinet.vue
<template>
<div class="cabinet-layout">
<div>CabinetLayout</div>
<slot />
</div>
</template>
<script setup>
import { useRoute } from '#imports';
const { name, meta } = useRoute()
onMounted(() => {
console.log({ name }) // При переходе на клиенте здесь будет "index"
console.log(meta.someMeta) // А здесь undefined
})
</script>
Если открыть демо, развернуть консоль и перейти с главной страницы
в личный кабинет, то будет видно, что в консоль выводится route.name
главной страницы, а не текущей (cabinet). Корректный же name
прилетает, только если, находясь в личном кабинете, перезагрузить страницу.
То есть если мы используем накстовский useRoute внутри
шаблонов, то при переходе по страницам, сопровождаемом сменой
шаблонов, мы получим не текущий роут (как ожидалось), а тот роут,
с которого мы зашли в приложение (в данном случае главная страница).
Почему так происходит, можно узнать вот здесь, здесь, здесь и здесь.
Решение
Наверно, самым правильным решением было бы вообще не использовать
useRoute для логики внутри шаблона.
Однако если этот вариант не подходит, то можно сделать вот так:
<template>
<div class="cabinet-layout">
<div>CabinetLayout</div>
<slot />
</div>
</template>
<script setup>
import { useRouter } from '#imports';
const { name } = useRouter().currentRoute.value
onMounted(() => {
console.log({ name })
})
</script>
или даже так:
<template>
<div class="cabinet-layout">
<div>CabinetLayout</div>
<slot />
</div>
</template>
<script setup>
import { useRoute } from 'vue-router';
const { name } = useRoute()
onMounted(() => {
console.log({ name })
})
</script>
Теперь при переходе между лейаутами в хуке onMounted всегда
будет доступен корректный текущий роут.
Источники
- https://nuxt-route-layout-issue.surge.sh
- https://github.com/nuxt/nuxt/issues/21340
- https://github.com/nuxt/nuxt/issues/28804
- https://github.com/nuxt/nuxt/issues/21283#issuecomment-1569963217
- https://github.com/nuxt/nuxt/issues/20674#issuecomment-1534739499
- https://nuxt.com/docs/api/composables/use-route
- https://router.vuejs.org/guide/advanced/composition-api