Некорректные данные 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 всегда будет доступен корректный текущий роут.

Источники

  1. https://nuxt-route-layout-issue.surge.sh
  2. https://github.com/nuxt/nuxt/issues/21340
  3. https://github.com/nuxt/nuxt/issues/28804
  4. https://github.com/nuxt/nuxt/issues/21283#issuecomment-1569963217
  5. https://github.com/nuxt/nuxt/issues/20674#issuecomment-1534739499
  6. https://nuxt.com/docs/api/composables/use-route
  7. https://router.vuejs.org/guide/advanced/composition-api