How to use Vue Router's lazy loading with vue-class-component? - vue.js

I'm still fairly new to Vue, and trying to implement lazy loaded routes on a project that uses class style components. Currently the components are defined for the routes using Webpack's dynamic import like so:
{
path: '/dashboard',
name: 'dashboard',
component: function() {
return import(
/* webpackChunkName: "dashboard" */ '../components/content-views/content-main/ContentDashboard.vue'
);
},
},
But this doesn't seem to be working, as loading the app with an empty cache downloads all of the chunks up front, resulting in an enormous app file. Reading the documentation, it looks like I need to define the class components as async by returning a Promise which resolves with the component. However, I'm honestly not sure if that's possible with a component which is a class definition, as opposed to a typical object-based one. The vue-class-component documentation doesn't mention this, but I'm wondering if there's some kind of alternative syntax for achieving this, or if I'm missing something else.
Any assistance appreciated!

This is how you can use lazy routing in vuejs
path: "/",
name: "index",
component: () =>
import("../views/Index.vue")

just as MaBbKhawaja said,
path: "/",
name: "index",
component: () =>
import("../views/Index.vue")
you can also assign chunkName like this
path: "/",
name: "index",
component: () =>
import(/* webpackChunkName: "Home" */ "../views/Index.vue")
The docs gives a better explanation

Related

How to use a single route in different contexts?

In Nuxt, I have an admin dashboard with a special layout (sidebar), where I use <NuxtChild> to render child routes:
Admin.vue
<NuxtChild :key="$route.path" />
Routes (simplified):
{
path: "/admin",
name: "admin",
component: Admin,
children: [
{
path: '/admin/event/create',
name: 'EventCreate',
component: EventCreate,
props: true
}
// many more routes...
]
}
Now, I want the EventCreate route to also be available alone, in a regular isolated context (NOT in the admin dashboard). This is simple enough with another route. This works fine:
{
path: '/event/create',
name: 'EventCreate',
component: EventCreate,
props: true
}
PROBLEM:
My routes config file will be too messy, with duplicated routes that essentially only differ by path.
Note: I do not use Nuxt's standard file-based routing. Instead all of my route's are defined in one central config file (for many reasons, and my preference). This is done using the Nuxt-Community router library: https://github.com/nuxt-community/router-module. The end result is essentially how Vue-Router works (ie routes defined in a config file).
QUESTION:
Is there a way to define a route once, and have it apply to different contexts (alone or as a child inside another route)?
On a higher level perhaps there's a better way to handle this context switching (plain page vs child-inside-dashboard). In any case, <NuxtChild> works well aside from this, and I wanted to keep using it.
Any suggestions; different idea?
it's easy you can use alias option of vue-router:
{
path: "/admin",
name: "admin",
component: Admin,
children: [
{
path: 'event/create', // not repeat path from root
alias: ['/event/create'],
name: 'EventCreate',
component: EventCreate,
props: true
}
// many more routes...
]
}
and one more note never use a path that starts with / and repeat the parent path in children it doesn't work at all it's verbose, confusing and potentially error-prone
Share route properties with an object
Based on comment by #Estus Flask, here's one way to have a cleaner routes definition:
Use a object to store shared properites, and use it in relevant routes, reducing duplication. Still, you must still have separate routes for contexts, which makes sense.
Object to hold shared properties:
const MyEventCreateRoute = {
component: EventCreate,
props: true,
// other stuff
}
Routes use the shared properties with spread operator:
// Basic route; not in dashboard:
{
path: '/event/create',
name: 'EventCreate',
...MyEventCreateRoute
}
// Route in admin dashboard; renders inside <NuxtChild>:
{
path: "/admin",
name: "admin",
component: Admin,
children: [
{
path: '/admin/event/create',
name: 'AdminEventCreate',
...MyEventCreateRoute
}
]
}

Optional route params with Vue Router

In my Vue 2.7.5 app (using Vue Router 3.5.4) I'm trying to define this route
{
path: '/messages/:messageId?/replies/:replyId?',
name: 'messages',
component: () => import('#/views/messages')
}
The intent is
to see all messages use /messages
to see a specific message use /messages/:messageId
To see a specific message and a specific reply to that message use /messages/:messageId/replies/:replyId
However, if I navigate to this route without specifying any route params using
<router-link :to="{name: 'messages'}">
Then the URL is resolved as /messages/replies, but I would like it to be resolved as /messages.
Essentially, what I want is: don't include /replies unless there's a replyId param, but I don't know how to express that.
One solution is to use the following instead:
<router-link :to="{ path: '/messages'}">
But I prefer to always refer to routes by name, because this gives me the flexibility to change the paths without breaking anything
The easiest solution for you is to remove /replies and only have path like this:
'/messages/:messageId?/:replyId?'
(Optional solution)
If removing that part of url is not an option and using named routes is a must, here is an alternative solution where you use two named routes. If the replyId is missing you can redirect before enter to the 2nd named route.
{
path: '/messages/:messageId?/replies/:replyId?',
name: 'message-replies',
component: () => import('#/views/messages'),
beforeEnter({ params }) {
if (!params.replyId) {
return {
name: 'messages',
params: {
messageId: params.messageId,
},
};
}
},
},
{
path: '/messages/:messageId?',
name: 'messages',
component: () => import('#/views/messages'),
},

Is there any way to have dynamic routes/components in vuejs router?

Hi beautiful Vuejs developers out there!
I have a little problem with routing many Vue components/pages dynamically. In this scenario I am using nested routes to have a couple of routes for my layout components and hundreds of child routes for my pages and as you can imagine I'll have to type many child routes statically or manually, and then add more when I need more child routes in the future code changes but I need a solution to simplify/solve this problem with more efficient/better way like adding those routes from what user types after the layout in the url... here is my example code code:
const routes: RouteRecordRaw[] = [
{
{
path: '/student',
component: () => import('layouts/StudentLayout.vue'),
children: [
{
path: 'dashboard',
component: () => import('pages/student/Dashboard.vue'),
},
{
path: 'profile',
component: () => import('pages/student/Profile.vue'),
},
],
},
}
As you see in this code I have a layout named Student and it has two children but I'll have to type manually hundreds of child routes for this layout and other layouts is there any way to dynamically set up those routes with what users enter after the layout name like /student/dashboard or /layout/page and match it with a component name? I mean like params in Angular, can I use the param value itself inside the router to say?
{
path: ':pagename',
component: (pagename) => import('pages/student/' + pagename + '.vue'),
},
let me know if there is an efficient way to solve this problem.
Thanks in advance!
I would, personally, not use this, or advise such an approach, nor have I done it, but this idea came to me when I read your question:
My best guess would be to have a handler component which renders a component dynamically based on a route parameter (say you have /:subpage as a child to /student and the handler component is set to that route), and an exception handler around that to show a 404 page when the user types in an inexistent/unsupported route.
For example, I would dynamically import the component by the route parameter into a predefined let (e.g. let SubpageComponent; outside the try catch block), have a try catch block around the dynamic import assignment for the respective error where catch would set the variable to a 404 page. Then I would add the SubpageComponent into the data() of the component doing the rendering of the route.
Edit
I've written out come code that, maybe, makes sense.
It's based on https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components
your routes definition, changed
const routes: RouteRecordRaw[] = [
{
path: '/student',
component: () => import('layouts/StudentLayout.vue'),
children: [
{
path: '/:subpage',
component: () => import('pages/student/SubpageRenderer.vue'),
props: true,
},
],
},
]
SubpageRenderer.vue
<script>
export default {
props: ['subpage'],
data() {
return {
currentSubpage: () => import(`./${subpage}.vue`)
}
}
}
</script>
<template>
<component :is="currentSubpage"></component>
</template>
Instead of using the currentSubpage import, you can also use the subpage route prop to bind :is if subpage is the name of a registered component.
Since this would get only "dashboard" from the route, you'd need some namespacing, like "student-dashboard" with the help of template literals. You could make currentSubpage into a template literal that creates the student-${subpage} name.
I'd probably recommend importing the options object of the component designated by the subpage route parameter instead of registering all the components - if you're registering them, you might as well use vue-router the usual way :)
Also, I only think this could work! It should be tested out, and perhaps casing should be kept in mind, and maybe the Layout suffix as well (subpage will probably be all lowercase, and you'll probably have the components named in PascalCase). After uppercasing the first letter, this could also obviously lead to both /student/Dashboard and /student/dashboard being valid routes

vueJS 3.x: navigate from page after HTML form-submit

Versions:
vueJS: 3.0.0
vuex: 4.0.2
Chrome: Version 94.0.4606.61 (Official Build) (x86_64)
One advantage of SPA frameworks like vueJS is that they offer some efficiencies in network consumption (ie, fewer server hits by delivering UI/UX assets to client in bulk, and hopefully minimizing server requests). But I'm running into a scenario where just the opposite happens: ie, I am required to revisit the server in order to navigate between vueJS components/views. This seems highly contradictory to the SPA ethos, and I'm suspicious something simple must be wrong in my setup. Details follow.
router/index.js:
import { createRouter, createWebHistory } from 'vue-router'
import Home from '#/views/Home.vue'
import Car from '#/views/Car.vue'
import Bike from '#/views/Bike.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '#/views/About.vue')
},
{
path: '/cars/new',
name: 'New Car',
component: Car
},
{
path: '/cars/:id',
name: 'Edit Car',
component: Car,
props: true
},
{
path: '/bikes/new',
name: 'New Bike',
component: Bike
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
Then in Car.vue component, I have a form-submit handler something like this:
handleSubmit(event) {
let form = event.target;
if (form.checkValidity()) {
// Add or update Car.
window.location.href = window.location.origin + process.env['BASE_URL'];
}
this.wasValidated = true
Rather than using window.location.href, I tried to use:
this.$router.push('Home');
But that had no effect. That is, the URL in the browser address bar began as something like http://localhost:8080/myapp/, and remained that way after the router-push.
I also tried pushing to other routes, like About; in that case, the browser address bar properly toggled to http://localhost:8080/myapp/about, but the page content remained the same!
Clearly, this cannot be the right behavior.
Can you suggest how to fix this?
this.$router.push('Home') tries to push 'Home' as a path, but there's no matching path in your router config, nor is there a fallback route (for 404s), so the route simply doesn't change.
If you meant to push the route by name, the $router.push() argument needs to be an object:
this.$router.push({ name: 'Home' })
If you prefer to use a path, the path of Home is actually /:
this.$router.push('/')

Vue Router: How to pass data to component being linked to?

I am using client side routing and have the route name be the object's name. I am linking to the Edit.vue component but if I want to render the age in that Edit component, how do I get that passed in? I know I have name accessible in the router params but I want the other fields in that object too, such as age.
App.vue
<div v-for="item in items">
<router-link :to="`/edit/${item.name}`"> Edit ${item.name} </router-link>
</div>
data() {
return {
items: [ {name: "Carl", age: 23}, { name: "James", age: 43}]
}
}
then in my router config, I have:
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/edit/:name",
name: "Edit",
component: () =>
import(/* webpackChunkName: "edit" */ "../views/Edit.vue"),
},
];
State management is the keyword, which might help for your further research.
Especially vuex, as the most popular Vue state management library probably makes sense in your case (https://www.npmjs.com/package/vuex). There are tons of tutorials out there.
If you don't want to use a state management library, you can implement a simple version of it on your own by saving the data in the localstorage or in the cookies. Or - if it's just about the age, you also could add it to the query params.
There is also a pretty well described SO answer for a similar question: Vue: shared data between different pages
This is not the best way to do it.
You could implement /edit/:name/:age. But what happens if you access the URL /edit/Carl/999?
You should fetch the data, such as name and age, by a unique user id instead: edit/:userid.