javascript Vue 3中嵌套路由的暂停-为什么内容会消失?

huwehgph  于 2023-05-16  发布在  Java
关注(0)|答案(2)|浏览(142)

bounty已结束。此问题的答案有资格获得+200声望奖励。赏金宽限期14小时后结束。chojnicki希望引起更多关注这个问题。

我正在使用Suspense,并在页面上获取异步数据。在初始化回退加载指示器是可见的,但在切换页面时,我会保留当前页面的内容,直到新页面准备就绪+nprogress条,因此网站的行为更像SSR。也有过渡之间,但它并不重要的问题。
而且效果很好。问题是当我的页面有子页面(嵌套的路由),也与Suspense。当我在像/demo/demo1这样的嵌套页面上导航到主页时,/demo模板的内容保持原样,但子页面demo1的内容消失了。我需要保留嵌套的Suspense,直到父Suspense准备就绪。有什么想法吗
我尽可能地简化代码。我在根应用程序和页面上使用相同的<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 s)。
繁殖步骤:
1.点击“关于”-“主页”停留,直到“关于”准备好,然后“关于”出现-工作正常。
1.点击“Demo”-出现“demo”根页面,出现嵌套“demo/demo 1”路由的回退-工作正常。
1.点击“主页”-“演示/演示1”的内容立即消失。

wtlkbnrh

wtlkbnrh1#

我不会说这是最好的答案,但在我看来,为了让<Suspense>工作,它的子组件必须是一个异步组件。在这种情况下,<RouterView>被呈现为非异步组件(即使其子组件是异步组件),因此根<Suspense>不会等待<RouterView>中的<Suspense>
作为一种解决方法,我认为我们可以缓存组件,这样当路由器被更改时(第三种情况),它看起来就不会消失。
我的缓存方法是在demo.vue文件中添加这个<script setup>

<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值,如果有一个新的Component不是falsy。如果它是falsy(意味着它还没有被挂载或不再被挂载),它将呈现组件的先前的最新缓存版本。我知道这是黑客,但在我看来,这是一个非常简单的解决办法。
This is the forked stackblitz如果你感兴趣的话

lhcgjxsq

lhcgjxsq2#

当您有嵌套路由时,它在复杂的项目中根本不起作用,正如您所注意到的那样,它可以很好地在单个级别上进行路由。
我为此奋斗了一段时间,尝试了许多组合,发现嵌套的悬念被打破了,这与被动用于路由器组件的key周围的功能故障有关,我尝试手动设置这一点,产生了各种结果和其他无法修复/恼人的问题,包括使用keep-alive
不能使用async setup真的很烦人和不幸,特别是在尝试API调用之前协商其他承诺时挂起,我特别喜欢代码的整洁程度。
我不得不接受async watch,其中我设置了一个loading = ref(true)状态,并将其传递给一个自定义的加载状态组件 Package 器,该 Package 器将I/O阻止内容并在内容主体中显示加载进度。

// 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>

如果你想的话,你也可以在那里拍过渡。
我还在考虑重新推导加载 Package 器和加载引用的重复,方法是将其移动到托管子router-view的父组件,并在子页面中使用emit来切换加载。我还没有尝试过这个,需要确保转换到其他页面时的初始状态为真。

相关问题