Search code examples
javascriptvue.jsvue-componentvuejs3vue-composition-api

I got blank when use async setup() in Vue3 with router-view


When I use async setup () in Vue 3, my component disappears. I used the solution found here: why i got blank when use async setup() in Vue3 ... it worked, but I have a blank page when I am using the router-view.

<template>
    <div v-if="error">{{error}}</div>
    <Suspense>
        <template #default>
            <router-view></router-view>
        </template>
        <template #fallback>
            <Loading />
        </template>
    </Suspense>
</template>

<script>
import Loading from "./components/Loading"
import { ref, onErrorCaptured } from "vue"

export default {
    name: 'App',
    components: { Loading },
    setup() {
        const error = ref(null)
        onErrorCaptured(e => {
            error.value = e
        })
    }
}
</script>

main.js:

import { createApp } from 'vue'
import router from "./router"
import App from './App.vue'

createApp(App).use(router).mount('#app')

When I replace router-view with one of my components, it shows up.

Router:

import { createWebHistory, createRouter } from "vue-router";
import Portfolio from "@/views/Portfolio.vue";
import Blog from "@/views/Blog/Blog.vue";
import Detail from "@/views/Blog/Detail.vue";
import NotFound from "@/views/NotFound.vue";

const routes = [
    {
        path: "/",
        name: "Home",
        component: Portfolio,
    },
    {
        path: "/blog",
        name: "blog",
        component: Blog,
    },
    {
        path: "/blog/:slug",
        name: "detail",
        component: Detail,
    },
    {
        path: "/:catchAll(.*)",
        component: NotFound,
    },
];

const router = createRouter({
    history: createWebHistory(),
    routes,
});


export default router;

Solution

  • <Suspense>'s default template should contain an async component (i.e., one with async setup()), but you've put <router-view> in there instead.

    You'd have to refactor the <Suspense> into its own wrapper component (e.g., HomeView.vue) that contains the async component, leaving <router-view> itself in App:

    <!-- App.vue -->
    <template>
      <router-view />
    </template>
    
    <!-- HomeView.vue -->
    <template>
      <Suspense>
        <template #default>
          <MyAsyncComponent />
        </template>
        <template #fallback>
          <Loading />
        </template>
      </Suspense>
    </template>
    

    Then update your router config to use that wrapper:

    const routes = [
      {
        path: '/'
        name: 'Home',
        component: HomeView
      },
      //...
    ]