Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suspense doesn't display fallback content and warning is output #560

Closed
seijikohara opened this issue Oct 27, 2020 · 16 comments
Closed

Suspense doesn't display fallback content and warning is output #560

seijikohara opened this issue Oct 27, 2020 · 16 comments

Comments

@seijikohara
Copy link

Version

3.0.2

Reproduction link

https://jsfiddle.net/seijikohara/jmw3rpue/

Steps to reproduce

No fallback content is displayed and warning is output.
The version of Vue is 3.0.2.

[Vue warn]: <Suspense> slots expect a single root node.

<router-view v-slot="{ Component }">
  <Suspense>
    <template #default>
      <component :is="Component" />
    </template>
    <template #fallback>
      <span>Loading...</span>
    </template>
  </Suspense>
</router-view>

What is expected?

Show fallback contents until the component of async setup is initialized.

What is actually happening?

No fallback content is displayed and warning is output.

@seijikohara
Copy link
Author

ref: vuejs/core#2143 (comment)

@edison1105
Copy link
Member

<router-view v-slot="{ Component }">
  <Suspense>
    <template>
	  <template #default>
		<component :is="Component" />
	  </template>
      
	  <template #fallback>
      	<span>Loading...</span>
      </template>
    </template>
    
  </Suspense>
</router-view>

Maybe you should do it like this.

@seijikohara
Copy link
Author

seijikohara commented Oct 27, 2020

@edison1105

<router-view v-slot="{ Component }">
  <Suspense>
    <template>
	  <template #default>
		<component :is="Component" />
	  </template>
      
	  <template #fallback>
      	<span>Loading...</span>
      </template>
    </template>
    
  </Suspense>
</router-view>

Maybe you should do it like this.

Not working.
https://jsfiddle.net/seijikohara/2p34jvyf/7/

@edison1105
Copy link
Member

  const Comp = defineComponent({
    render() {
      return h(
        'div',
        {
          class: 'child'
        },
        this.$slots.default({ Component: hello })
      )
    }
  })

I tried to use Comp instead of router-view. it works fine.
maybe its a bug of router-view.

@LinusBorg
Copy link
Member

/cc @posva

@seijikohara
Copy link
Author

I also tried the case without using router-view and was able to confirm that the fallback content is displayed.
https://jsfiddle.net/gck975e2/

@LinusBorg
Copy link
Member

I'll transfer this to the vue-router repo

@LinusBorg LinusBorg transferred this issue from vuejs/core Oct 28, 2020
@cexbrayat
Copy link
Member

@seijikohara You may need to add a timeout on your suspense declaration (since changes in Vue 3.0.0-rc.12, see the discussion in vuejs/core#2142).

Something like <Suspense timeout="0"> should work to display the fallback content.

The warning is indeed painful, and is an issue in vue-next @LinusBorg
I wanted to fix it and opened vuejs/core#2337 but I need some help to finish the PR.

@seijikohara
Copy link
Author

@cexbrayat

Something like <Suspense timeout="0"> should work to display the fallback content.

I added timeout="0" to Suspense and the fallback was displayed. Thanks.
https://jsfiddle.net/zmpvrxq4/

@posva posva closed this as completed Nov 3, 2020
@risha700
Copy link

risha700 commented Jan 10, 2021

this works well for me after many tries

    <RouterView name="default" v-slot="{ Component, route }">
      <transition :name="route.meta.transition" mode="out-in" :duration="300" :key="route.path">
        <Suspense >
          <template #default>
            <component :is="Component" :key="route.path"/>
          </template>
          <template #fallback>
            <div>
              Loading...
            </div>
          </template>
        </Suspense>
      </transition>
    </RouterView>

Although it re-mounts with every route request. any cleaner ideas!

@MCYouks
Copy link

MCYouks commented Jan 17, 2022

I could fixed the problem by implementing <Suspense timeout="0"> and by importing { defineAsyncComponent } from vue in the router.js file

// @/router.js

import { createRouter, createWebHistory } from "vue-router";
import { defineAsyncComponent } from "vue";

export const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: "/",
      name: "Main",
      component: defineAsyncComponent(() => import("@/views/Main.vue")),
    }
  ]
})

And in the App.vue file:

<template>
  <router-view v-slot="{ Component }">
    <suspense timeout="0">
      <!-- Default -->
      <template #default>
        <component :is="Component" />
      </template>

      <!-- Loading -->
      <template #fallback>
        <Loading />
      </template>
    </suspense>
  </router-view>
</template>

<script>
import Loading from "@/views/Loading.vue";

export default {
  components: {
    Loading
  }
};
</script>

And it works like charm ✨

The only drawback of this solution is that you get the following warning message prompted in the console:

vue-router.esm-bundler.js:72 [Vue Router warn]: Component "default" in record with path "/" is defined using "defineAsyncComponent()". Write "() => import('./MyPage.vue')" instead of "defineAsyncComponent(() => import('./MyPage.vue'))".

Does someone know how to prevent this warning message from being prompted?

Thanks 🙂

@dezinerdude
Copy link

dezinerdude commented Apr 29, 2022

The best way to use RouterView, Suspense, Transition and KeepAlive together is as mentioned on the Vue Docs site under the Suspense Component page

@jackysee
Copy link

jackysee commented Jun 23, 2022

Does someone know how to prevent this warning message from being prompted?

You can get away with no warning by:

/*
A factory function to create a dynamic route 
which is a functional component
with its immediate child the async component
*/
const lazyRoute = (f) => () => Promise.resolve(() => h(defineAsyncComponent(f)));


//...

  routes: [
    {
      path: "/",
      name: "Main",
      component: lazyRoute(() => import("@/views/Main.vue"))
    }
  ]

@jaawaad
Copy link

jaawaad commented Mar 7, 2023

I fixed this issue with the wrapping of the <Component /> in <div> and it's work for me.

  <router-view v-slot="{ Component }">
    <suspense timeout="0">
      <div>
        <component :is="Component"></component>
      </div>
      <template #fallback>
        <div>
          Loading...
        </div>
      </template>
    </suspense>
  </router-view>

@wtto00
Copy link

wtto00 commented Mar 21, 2023

<RouterView v-slot="{ Component }">
  <template v-if="Component">
    <Transition mode="out-in">
      <KeepAlive>
        <Suspense>
          <!-- 主要内容 -->
          <component :is="Component"></component>

          <!-- 加载中状态 -->
          <template #fallback>
            正在加载...
          </template>
        </Suspense>
      </KeepAlive>
    </Transition>
  </template>
</RouterView>

Add this works for me.

<template v-if="Component">
...
</template>

@paprika135
Copy link

<router-view v-slot="{ Component }">
  <Suspense>
    <template>
	  <template #default>
		<component :is="Component" />
	  </template>
      
	  <template #fallback>
      	<span>Loading...</span>
      </template>
    </template>
    
  </Suspense>
</router-view>

Maybe you should do it like this.
Immediately error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests