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

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

Closed
onx2 opened this issue Aug 25, 2020 · 15 comments
Closed

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

onx2 opened this issue Aug 25, 2020 · 15 comments

Comments

@onx2
Copy link

onx2 commented Aug 25, 2020

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?

@onx2
Copy link
Author

onx2 commented Aug 25, 2020

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

@onx2 onx2 closed this as completed Aug 25, 2020
@baryla
Copy link

baryla commented Aug 25, 2020

@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 :)

@onx2
Copy link
Author

onx2 commented Aug 26, 2020

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"))
  }
]

[Section about generate routes removed to prevent misinformation]

If someone knows how to make dynamically generated routes work, please comment.

@MatthiasGrandl
Copy link

@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
Copy link
Author

onx2 commented Nov 2, 2020

@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?

@metatarz
Copy link

@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,
    })
      )
    )
  }
]

@onx2
Copy link
Author

onx2 commented Dec 1, 2020

@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

@EngHell
Copy link

EngHell commented Dec 28, 2020

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;

@Epic-Deno
Copy link

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`))
  });
}

I using ' defineAsyncComponent(() => import(./routes/${route}.vue))', but it dones't work.
WeChatecaa2206af2215034f40674ead5d314f
I want to generate a dynamic route like webpack
WechatIMG1605

@onx2
Copy link
Author

onx2 commented Jan 12, 2021

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`))
  });
}

I using ' defineAsyncComponent(() => import(./routes/${route}.vue))', but it dones't work.
WeChatecaa2206af2215034f40674ead5d314f
I want to generate a dynamic route like webpack
WechatIMG1605

To the best of my knowledge now, generated routes won't work as I defined above because it cannot know what the parameter used in the function will be. I can update my original post.

@yyx990803
Copy link
Member

Please see latest glob import feature: https://vitejs.dev/guide/features.html#glob-import

@simultsop
Copy link

simultsop commented Feb 5, 2021

@yyx990803

when using globEager like
`import { RouteRecordRaw } from 'vue-router'

var routes: Array = []

const InstalledAppRoutes = import.meta.globEager('../../apps/*/routes.ts')

for (const App in InstalledAppRoutes) {
const AppRoutes = InstalledAppRoutes[App]
routes = routes.concat(AppRoutes.default)
}

export default routes`

in runtime it says loader not a function

image

@simultsop
Copy link

@yyx990803

when using globEager like
`import { RouteRecordRaw } from 'vue-router'

var routes: Array = []

const InstalledAppRoutes = import.meta.globEager('../../apps/*/routes.ts')

for (const App in InstalledAppRoutes) {
const AppRoutes = InstalledAppRoutes[App]
routes = routes.concat(AppRoutes.default)
}

export default routes`

in runtime it says loader not a function

image

Darn, due to having multiple routes.ts files on some forgot to update the import promise as mentioned above () => import(comp).
now all routes files are loaded and components resolve. Marvelous.

@fjeddy
Copy link

fjeddy commented Feb 14, 2021

Hate that no one answered the original question :/ How do you set a custom name on the chunk.

@EngHell
Copy link

EngHell commented Feb 22, 2021

This is less or moe due to the title of the question, then when people searches for this on google this is one of the first results.

@github-actions github-actions bot locked and limited conversation to collaborators Jul 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants