Некорректные данные 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',
meta: {
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