'Vue 3 dynamic components at router level

Dynamic imports is needed for me, eg. i have 10 layouts, but user only visited 3 layouts, I should not import all of the layouts, since its consumed unnecessary resources.

Since its dynamic import, each time i switch between Login & Register path <RouterLink :to"{name: 'Login'}" /> & <RouterLink :to"{name: 'Register'}" />, I got rerender or dynamic import the layout again.

My question is what is the better way to handle it, without rerender or dynamic import the layout again? Or can I save the dynamic import component into the current vue 3 context?

App.vue this is my app with watching the router and switch the layout based on route.meta.layout

<template>
  <component :is="layout.component" />
</template>

<script>
import DefaultLayout from "./layout/default.vue";

import {
  ref,
  shallowRef,
  reactive,
  shallowReactive,
  watch,
  defineAsyncComponent,
} from "vue";

import { useRoute } from "vue-router";

export default {
  name: "App",
  setup(props, context) {
    const layout = shallowRef(DefaultLayout);
    const route = useRoute();
    watch(
      () => route.meta,
      async (meta) => {
        if (meta.layout) {
          layout = defineAsyncComponent(() =>
            import(`./layout/${meta.layout}.vue`)
          );
        } else {
          layout = DefaultLayout;
        }
      },
      { immediate: true }
    );
    return { layout };
  },
};
</script>

router/index.js this is my router with layout meta

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

const routes = [
  {
    path: "/",
    name: "Home",
    component: Home,
  },
  {
    path: "/login",
    name: "Login",
    meta: {
      layout: "empty",
    },
    component: function () {
      return import(/* webpackChunkName: "login" */ "../views/Login.vue");
    },
  },
  {
    path: "/register",
    name: "Register",
    meta: {
      layout: "empty",
    },
    component: function () {
      return import(/* webpackChunkName: "register" */ "../views/Register.vue");
    },
  },
  { path: "/:pathMatch(.*)", component: NotFound },
];

const router = createRouter({
  history: createWebHistory(import.meta.env.VITE_GITLAB_BASE_PATH),
  routes,
  scrollBehavior(to, from, savedPosition) {
    // always scroll to top
    return { top: 0 };
  },
});

export default router;


Solution 1:[1]

You could use AsyncComponent inside the components option and just use a computed property that returns the current layout, this will load only the current layout without the other ones :

components: {
        layout1: defineAsyncComponent(() => import('./components/Layout1.vue')),
        layout2: defineAsyncComponent(() => import('./components/Layout2.vue')),
},

Solution 2:[2]

Had this issue and Thorsten Lünborg of the Vue core team helped me out. add the v-if condition and that should resolve it.

  <component v-if="layout.name === $route.meta.layout" :is="layout">

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Boussadjra Brahim
Solution 2 nytro04