Приостановка в Vue 3 на вложенных маршрутах — почему исчезает контент?

Я использую Suspense и извлекаю асинхронные данные на страницах. При инициализации виден индикатор резервной загрузки, но при переключении страниц я сохраняю текущее содержимое страницы, пока новая страница не будет готова + полоса nprogress, поэтому сайт ведет себя больше как SSR. Есть еще Переход между, но это не важно для этого вопроса.

И это прекрасно работает. Проблема в том, что на моей странице есть дочерние страницы (вложенные маршруты), также с Suspense. Когда я нахожусь на вложенной странице, такой как /demo/demo1, и перехожу на главную, содержимое шаблона /demo остается таким, каким должно быть, но содержимое подстраницы demo1 исчезает. Мне нужно сохранить вложенную приостановку, пока родительская приостановка не будет готова. Есть идеи?

Я максимально упростил код. Я использую один и тот же код <RouterView> как в корневом приложении, так и на странице.

<RouterView v-slot = "{ Component }">
  <template v-if = "Component">
    <Suspense>
      <component :is = "Component" />
      <template #fallback>
        Loading...
      </template>
    </Suspense>
  </template>
</RouterView>

Вот живое воспроизведение: https://stackblitz.com/edit/vitejs-vite-gg6kqo?file=src/pages/demo/demo1.vue (нет nprogress, просто подождите 1 с между страницами).

Шаги к размножению:

  1. Нажмите «О программе» — «домашняя страница» остается до тех пор, пока не будет готово «о программе», а затем появляется «о программе» — работает нормально.
  2. Нажмите на "Demo" - появится корневая страница "demo", появится запасной вариант для вложенного маршрута "demo/demo1" - работает нормально.
  3. Нажмите «Главная» — содержимое «demo/demo1» немедленно исчезнет.

Может еще не работает. github.com/vuejs/core/issues/5637

Kalimah 10.05.2023 09:46

Vue v3.3.2, теперь это работает, как я и ожидал.

Marc 17.05.2023 16:03

@Марк странно. Я протестировал альфа-версию, прежде чем спросить здесь, потому что в 3.3.0 реализована опция «приостановка». Но это ничего не изменило в моем вопросе. Затем снова протестирую его с 3.3.2, с этой опцией и без нее. Спасибо.

chojnicki 18.05.2023 17:04

Я заметил журнал изменений в Nuxt3, в котором явно упоминается о повышении его версии зависимости Vue до 3.3.2, потому что было выпущено исправление ошибки вложенных страниц, но я не видел документированных изменений в самом журнале изменений Vue 3 для этой проблемы.

Marc 18.05.2023 20:45

@Marc проверил это снова, и после перехода на 3.3.4 в моей репродукции или реальном приложении ничего не изменилось: / Подстраница по-прежнему исчезает после переключения корневой страницы.

chojnicki 25.05.2023 21:57

Я обновил свой ответ дальше по теме тем, что я делаю, возможно, там есть что-то, что могло бы ответить на это, это вызывающе работает для меня, которое я запустил на прошлой неделе, как только заметил дискретное исправление. Я предполагаю, что они вполне в этом заинтересованы, потому что официально производство не готово.

Marc 26.05.2023 12:14
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
6
202
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я бы не сказал, что это лучший ответ, но мне кажется, что для того, чтобы <Suspense> работал, его дочерний элемент должен быть асинхронным компонентом. В этом случае <RouterView> отображается как неасинхронный компонент (даже несмотря на то, что его дочерние элементы являются асинхронными компонентами), поэтому корень <Suspense> не будет ждать <Suspense> внутри <RouterView>.

В качестве обходного пути, я думаю, мы можем кэшировать компонент, чтобы он не выглядел так, как будто он исчез при смене маршрутизатора (третий случай).

Мой метод кэширования заключается в добавлении этого <script setup> в файл demo.vue:

<script setup>
import { ref } from 'vue';

const CachedComponent = ref(null);

function cacheComponent(Component) {
  CachedComponent.value = Component || CachedComponent.value;
}
</script>

А затем измените <RouterView> внутри того же файла на это:

    ...
    <RouterView v-slot = "{ Component }">
      {{ cacheComponent(Component) }}
      <template v-if = "Component || CachedComponent">
        <Suspense>
          <component :is = "Component || CachedComponent" />
          <template #fallback>
            <div style = "font-size: 1.5rem">Loading Demo Nested...</div>
          </template>
        </Suspense>
      </template>
    </RouterView>
    ...

Строка {{ cacheComponent(Component) }} будет кэшировать компонент при его рендеринге (она используется таким образом, потому что <RouterView> не имеет обратного вызова, когда его слоты смонтированы). Строка CachedComponent.value = Component || CachedComponent.value; обновит значение CachedComponent, если есть новый компонент, который не является ложным. И если это ложно (это означает, что он еще не смонтирован или больше не смонтирован), он отобразит предыдущую последнюю кэшированную версию компонента. Я знаю, что это хакерство, но, на мой взгляд, это очень простой обходной путь.

Это ответвление стека, если вам интересно.

Я уже об этом думал, но мне не понравилась идея кэширования всего экземпляра компонента и выполнения этого метода внутри шаблона, потому что это анти-шаблон. Но если других вариантов не будет, попробую этот. Просто нужно немного рабочего времени, чтобы проверить это. Спасибо!

chojnicki 10.05.2023 13:46

Обновление Vue 3.3.2 — 17.05.2023

Я только что протестировал эту версию, и вложенные страницы маршрутизатора с async setup работают.

Из того, что я вижу, страницы вложенных страниц маршрутизатора больше не отображают пустые страницы с предупреждением консоли о необходимости завернуть в Suspense.

Вместо этого верхний уровень Suspense теперь обрабатывает дочерние страницы, не предполагая, что вокруг вложенных Suspense должен быть RouterView.

// App.vue
<RouterView v-slot = "{ Component, route }">
    <template v-if = "Component">
        <KeepAlive>
            <Suspense>
                <component :is = "Component"/>
                <template #fallback>
                    LOADING...
                </template>
            </Suspense>
        </KeepAlive>
    </template>
</RouterView>

// pages/nested-path/index.vue

<div>
  <RouterView v-slot = "{ Component, route }">
    <component :is = "Component" :key = "route.path"/>
  </RouterView>
</div>

Предыдущий ответ

Это просто не работает в настоящее время для сложных проектов, когда у вас есть вложенные маршруты, оно отлично работает с маршрутизацией на одном уровне, как вы заметили.

Я некоторое время боролся с этим, перепробовал много комбинаций и обнаружил, что вложенная приостановка не работает, это связано с нарушением функциональности вокруг key, который пассивно используется для компонента маршрутизатора, я попытался установить это вручную, что дало различные результаты и другие неустранимые / раздражающие проблемы, включая любое использование с keep-alive.

Невозможность использовать async setup была действительно раздражающей и досадной, особенно для приостановки во время согласования других промисов перед попыткой вызова API, мне особенно нравится, насколько чистым был бы код.

Мне пришлось согласиться на асинхронный watch, где я задал loading = ref(true) состояние, переданное пользовательской оболочке компонента состояния загрузки, которая будет блокировать ввод-вывод контента и отображать ход загрузки в теле контента.

// myPage.vue

<script setup lang = "ts">

const props = defineProps<{
    id: string
}>()

const loading = ref(true)

watch(props, async () => {

  loading.value = true

  await myApi.endpoint.get(props)

  loading.value = false

}, {immediate: true})

</script>

<template>
  <content-loader :loading = "loading">
    <div>
      My page content
    </div>
  </content-loader>
</template>
// ContentLoader.vue

<script setup lang = "ts">

withDefaults(defineProps<{
    loading: boolean
}>(), {
    loading: true
})

</script>

<template>

    <div class = "d-flex fill-height justify-center align-center">

        <template v-if = "$props.loading">

            <span>Content loading...

        </template>

        <template v-else>
            <slot/>
        </template>

    </div>

</template>

Если хотите, вы также можете добавить туда переходы.

Я также думал о повторном выводе повторения оболочки загрузки и ссылки на загрузку, переместив ее в родительский компонент, содержащий дочерний элемент router-view, и используя emit на дочерней странице для переключения загрузки. Я не пробовал это, нужно убедиться, что начальное состояние истинно при переходе на другие страницы.

Другие вопросы по теме

Похожие вопросы

«Предупреждение: Prop `className` не соответствует» при настройке поставщика темы в Next.js
Невозможно произвести щелчок правой кнопкой мыши по элементу в пользовательском событии библиотеки тестирования реакции v14
Фильтр массива javascript не работает с реакцией
Как я могу добавить пользовательский тип в NextApiRequest в Next.js?
Использование липкой позиции с дочерними гибкими элементами вызывает мерцание, поэтому вместо этого используется фиксированная позиция и позиция полосы прокрутки Javascript
Как изменить элемент в таблице с помощью JavaScript
Apache ECharts не скрывает указатель оси при программном скрытии всплывающей подсказки
Есть ли способ передать переменную в качестве значения в API листов Google?
Почему мой функциональный компонент React не перерисовывается, когда я изменяю (логическое) значение свойства состояния?
Как запустить анимацию в ячейке таблицы при изменении значения