I'm trying to use parameter only as a route as I need URL like localhost:8080/road. But, it's not working properly.
My code:
{
path: "/:id",
name: "id",
component: Blog
},
Whenever I enter a url like localhost:8080/dashboard or any other URL, it uses the Blog component. How can I solve it?
If your goal is to cut down on lines of code in your router definition, you could optimise this somewhat with an array of component names mapped to route definitions. For example
const components = ["Blog", "Road", "Dashboard"]
// creates kebab-cased slugs from Pascal cased component names
const toSlug = component =>
component.replace(/(?<=\w)[A-Z]/g, c => `-${c}`).toLowerCase()
const routes = components.map(name => ({
name,
path: toSlug(name),
component: () => import(`#/components/${name}.vue`)
})
Using static parts in the import path like #/components/ and .vue help Webpack optimise bundling.
Related
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
With Nuxt.js, in my router.js I'm trying to import my route component thus:
{
path: '/',
component: () => import(/* webpackChunkName: "index" */ '~/pages/index.vue')
}
I get this error:
render function or template not defined in component: anonymous
I came across someone else's Nuxt.js project where they add the following at the end, and with this it works:
{
path: '/',
component: () => import(/* webpackChunkName: "index" */ '~/pages/index.vue').then(m => m.default || m)
}
The returned object of my import() looks like this:
In a another Vue.js, non-Nuxt project, the same kind of import() looks like that:
In both cases, the component is nested in some "default" object. But with Nuxt.js it seems you must import that object explicitly, whereas with regular Vue.js you don't have to specify that.
Why?
Regarding why the module is nested inside a { default } object: it's because Nuxt uses ES6 style modules. ES6 style modules are written the following:
export default {
// some object
};
Default is not the only property you can have. More syntax.
Regarding why vue-router are working without the .default, it's because the code is more versatile than the code used by Nuxt. Nuxt already ships it's own builder and always uses ES6 modules.
On the other hand, vue-router does not know about how it will be built. So to be easier to use, vue-router automatically checks if the module is ES6 or not.
const resolve = once(resolvedDef => {
if (isESModule(resolvedDef)) {
resolvedDef = resolvedDef.default
}
// ...
}
Source: code of vue-router.
As you may already know, it's very uncommon to use a router.js file in a Nuxt project, because Nuxt already has its own routing system which splits your code into chunks for each page. Documentation here.
The lazy component in vue/webpack seem to be wrong or I miss confuse about the terms.
To do lazy loading of component I use the keyword import and webpack should split this component to sepeate bundle, and when I need to load this component webpack should take care of it and load the component.
but in fact, webpack does make sperate file, but it loaded anyway when the application is running. which is unexpected.
For example I just create a simple vue application (using the cli) and browse to localhost:8080/ and the about page should be loaded (by default) using the import keyword.
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')
}
]
So This is by design? I load every time the file I do not need right now (the page about). and if I have 200 pages, when I'll fetch 200 files I dont need. how that ends up? that did I save here by using the import key?
In vuetify for example they uses import key, but the files are loaded anyway and not by demand.
You can also avoid component prefetch using one of the webpack "magic" comments (https://webpack.js.org/guides/code-splitting/#prefetchingpreloading-modules), eg.
components: {
MyComponent: () => import(/* webpackPrefetch: false */ './MyComponent.vue')
}
Feel free to read more about this Vue optimization below:
https://vueschool.io/articles/vuejs-tutorials/lazy-loading-individual-vue-components-and-prefetching/
If you're referring to the default app template from Vue CLI, then you're actually observing the prefetch of the About page, intended to reduce load times for pages the user will likely visit.
If you want to avoid this performance optimization, use this Vue config:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugins.delete('prefetch')
config.plugins.delete('preload')
}
}
For troubleshooting reference, Chrome's Network panel includes an Initiator column, which provides a clickable link to the source file that triggered the network call. In this case of the about.js, the source file looks like this:
try using vue-lazyload maybe it can help and for <script> tags you can try async defer it helps in website speed optimizations
I would like to render the same component for all languages. The translation will be done inside the component. The url and component relationship should look like so:
pseudocode:
browserurl: "/blogpost_1"
nuxtcomponent: "blogpost_1"
browserurl: "/en/blogpost_1"
nuxtcomponent: "blogpost_1"
browserurl: "/randompage"
nuxtcomponent: "randompage"
browserurl: "/en/randompage"
nuxtcomponent: "randompage"
My approach was to do the following, unfortunately i cant find a way to access pathMatch.
router: {
extendRoutes(routes, resolve){
routes.push({
path: 'en/*',
component: resolve(__dirname, '/' + somehow.access.pathMatch )
})
}
}
I don't think you can resolve component dynamically. component: expects component instance or Promise. Once the promise resolves, result is cached. I can imagine solution using navigation guards and switching components "by hand" but that would require to import views by hand etc.
So your best bet is to rewrite paths generated by nuxt to include optional path segment (from /about to /:lang(en|jp)?/about so it accepts paths like /en/about) - your component then receives lang parameter which will be empty for /about otherwise it will contain language....
Define available translations in meta
Rewrite the paths of pages with translations
In your page:
<script>
export default {
meta: {
translations: ['en', 'jp']
}
}
</script>
In Nuxt config (pseudo - not tested)
router: {
extendRoutes(routes, resolve) {
const routesWithTranslation = routes.filter(i => i.meta.translations && i.meta.translations.length > 0);
routesWithTranslation.forEach(route => {
const segment = `/:lang(${route.meta.translations.join("|")})?`
route.path = segment + route.path
})
}
I have a use case for needing the id part of a vue route to contain unescaped forward slashes.
My current route looks like this:
{
path: '/browse/:path*',
component: browse,
name: 'browse',
displayName: 'Browse',
meta: { title: 'Browse' },
},
So when a user browses to the above url, the browse component is shown.
However, i want to use the id part of the path (:path*) to contain a nestable fielsystem like path to be consumed by my browse page.
For example the url /browse/project/project1 would take me two levels down in my tree to the project1 item.
Now, the problem i'm running into is that vue router is escaping my ids (path) when navigating programatically, and my url ends up like this: /browse/project%2Fproject1. This is non-ideal and does not look nice to the end user. Also, if the user does browse to /browse/project/project1 manually the app will work correctly and even keep the original encoding in the url bar.
So i could resolve this my making an arbitrary number of child paths and hope that the system never goes over these, but thats not a good way to solve my problem.
I should also clarify that the application will not know anything about the path after /browse as this is generated dynamically by the api that powers the app.
Is there a native way in vue-router to handale this? or should i change up how im doing things.
There is a more elegant solution without workarounds.
Vue router uses path-to-regexp module under the hood and constructions like
const regexp = pathToRegexp('/browse/:path*')
// keys = [{ name: 'browse', delimiter: '/', optional: true, repeat: true }]
https://github.com/pillarjs/path-to-regexp#zero-or-more
const regexp = pathToRegexp('/browse/:path+')
// keys = [{ name: 'browse', delimiter: '/', optional: false, repeat: true }]
https://github.com/pillarjs/path-to-regexp#one-or-more
set repeat flag to true. Any array parameter with repeat flag will be joined with the delimiter (default '/').
So you can pass a splitted array ['project','project1'] instead of 'project/project1' into router.push():
router.push( {name: 'browse', params: {path: ['project','project1']}} );
or
router.push( {name: 'browse', params: {path: 'project/project1'.split('/')}} );
So I managed to 'fix' this with a bit of a hack.
When creating my Vue router instance I am attaching a beforeEach function to replace any outgoing encodings of '/'. This will send the 'correct' URL I am looking for to the client.
const router = new Router({
mode: 'history',
routes,
});
router.beforeEach((to, from, next) => {
// hack to allow for forward slashes in path ids
if (to.fullPath.includes('%2F')) {
next(to.fullPath.replace('%2F', '/'));
}
next();
});
I just stumbled over your question while facing a similiar problem.
Think this is because an id shall identify one single resource and not a nested structure/path to a resource.
Though I haven't solve my problem yet, what you probably want to use is a customQueryString:
https://router.vuejs.org/api/#parsequery-stringifyquery
https://discourse.algolia.com/t/active-url-with-vue-router-for-facet-and-queries/3399
I fixed it by creating helpers for generating hrefs for :to attributes of vue router link.
First i made router accessible for my new helper service like here Access router instance from my service
Then i created router-helpers.js and here i made my helpers, here is an example
import Vue from 'vue'
import router from '../router.js'
// replace %2F in link by /
const hrefFixes = function(to) {
return to.replace(/%2F/g, '/')
}
// my link helper
Vue.prototype.$linkExample = attr => {
// create "to" object for router resolve
const to = { name: `route-name`, params: { param1: attr } }
// this will resolve "to" object, return href param as string
// and then i can replace %2F in that string
return hrefFixes(router.resolve(to).href)
}
Just include this service once in your Vue application an then just use this helper like this
<router-link :to="$linkExample(attr)">text</router-link>