Vite: Question: How do I code split by route using vue-router@next?

Created on 25 Aug 2020  路  8Comments  路  Source: vitejs/vite

I'd like to lazy load routes / create a chunk for each route but I can't figure out how to make it work. In Webpack I can use a dynamic import with a "magic comment" to name the chunk.

Something like this would be nice but magic comments are something I don't _really_ care about, I'd just like the output of my build to be split by route and to have the dynamic imports reference the .js file.

import { RouteRecordRaw } from "vue-router"

export const routes: Array<RouteRecordRaw> = [
  {
    name: "About",
    path: "/about",
    component: () => import(/* webpackChunkName: "about" */, "../views/About.vue")
  }
]

This thread seems to indicate it works now using rollup-plugin-dynamic-import-variables but using a dynamic import in the router doesn't ouput what I'd expect -- is there something else I need to configure?

Most helpful comment

Sure! I simply changed it to the following:

import { defineAsyncComponent } from "vue"
import { RouteRecordRaw } from "vue-router"

export const routes: Array<RouteRecordRaw> = [
  {
    name: "About",
    path: "/about",
    component: defineAsyncComponent(() => import("../views/About.vue"))
  }
]

It's pretty nice to be able to dynamically generate async routes too (I think Webpack might be able to do this but I haven't tried).

// pseudo code

import * as routes from "./routes"

function generateRoutes() {
  return Object.keys(routes).map((route) => ({
    name: route,
    path: `/${kebabCase(route)}`,
    component: defineAsyncComponent(() => import(`./routes/${route}.vue`))
  });
}

All 8 comments

Nevermind... I just didn't read through the post / docs entirely.

@onx2 even still, I think it would be nice if you could share your findings for people coming from Google etc. and looking for an answer :)

Sure! I simply changed it to the following:

import { defineAsyncComponent } from "vue"
import { RouteRecordRaw } from "vue-router"

export const routes: Array<RouteRecordRaw> = [
  {
    name: "About",
    path: "/about",
    component: defineAsyncComponent(() => import("../views/About.vue"))
  }
]

It's pretty nice to be able to dynamically generate async routes too (I think Webpack might be able to do this but I haven't tried).

// pseudo code

import * as routes from "./routes"

function generateRoutes() {
  return Object.keys(routes).map((route) => ({
    name: route,
    path: `/${kebabCase(route)}`,
    component: defineAsyncComponent(() => import(`./routes/${route}.vue`))
  });
}

@onx2 does that work for you in production builds? During development it works fine, but in production it can't resolve the dynamic imports.

@onx2 does that work for you in production builds? During development it works fine, but in production it can't resolve the dynamic imports.

I haven't tested in a production environment but I can test serving the prod build locally and let you know. Are you trying to generate routes or hard code them?

@onx2 Vue router needs component to return a promise. So it should be instead:

import { defineAsyncComponent } from "vue"
import { RouteRecordRaw } from "vue-router"

export const routes: Array<RouteRecordRaw> = [
  {
    ...
    component: () => new Promise((resolve)=> resolve(defineAsyncComponent({
        loader: () => import("whatever"),
        loadingComponent: Spinner,
    })
      )
    )
  }
]

@sirdmon @MatthiasGrandl

Thanks for the heads up! I haven't had to time to do any testing or to keep up with the evolving API's -- does the following work for production as well? _(no options defineAsyncComponent)_

https://next.router.vuejs.org/guide/advanced/lazy-loading.html#lazy-loading-routes

The component (and components) option accepts a function that returns a Promise of a component and Vue Router will only fetch it when entering the page for the first time, then use the cached version.

import { defineAsyncComponent } from "vue"
import { RouteRecordRaw } from "vue-router"

export const routes: Array<RouteRecordRaw> = [
  {
    name: "About",
    path: "/about",
    component: () => new Promise(resolve => resolve(defineAsyncComponent(() => import("../views/About.vue"))))
  }
]

This seems pretty verbose for lazy loading / dynamic imports. Does defineAsyncComponent not return what we need?

const thing = () => import("./components/HelloWorld.vue"); is defined as () => Promise<any> and defineAsyncComponent's type def is:

export declare function defineAsyncComponent<T extends Component = {
    new (): ComponentPublicInstance;
}>(source: AsyncComponentLoader<T> | AsyncComponentOptions<T>): T;

So I would assume defineAsyncComponent(() => import("../views/About.vue")) to be invoked and defined as () => Promise<any>;?

A potentially helpful ref: https://v3.vuejs.org/guide/migration/async-components.html#_3-x-syntax

None of the mentioned aproaches work with vite build but do work with vite

vite version:

user@debian:~/hbadmin$ yarn vite -v
yarn run v1.22.5
warning package.json: No license field
$ /home/user/hbadmin/node_modules/.bin/vite -v
vite/1.0.0-rc.13 linux-x64 node-v15.0.1
Done in 2.07s.

vue-router version: 4.0.1
vue version: 3.0.4

router/index.js

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

const routes = [
    {
    path: "/",
    name: "Home",
    component: () => new Promise(resolve => resolve(defineAsyncComponent(() => import("/@/views/Home.vue"))))
  },
];

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

export default router;
Was this page helpful?
0 / 5 - 0 ratings