When we create our Vue application, one of the things we must configure, if our app has several pages, is the router.
Vue has its own router that is good enough so that you don’t need to use any other, and it is configured in a way similar to this:
import Vue from 'vue';
import Router from 'vue-router';
import Login from '../pages/Login';
import Home from '../pages/Home';
import Article from '../pages/Article';
Vue.use(Router);
new Router({
routes: [
{
path: '/login',
name: 'login',
component: Login,
},
{
path: '/',
name: 'home',
component: Home,
meta: {
auth: true,
},
},
{
path: '/articulo/:slug',
name: 'home',
props: true,
component: Article,
meta: {
auth: true,
},
},
],
});
As we can see, each route is defined in an Object within an Array, indicating the path it will respond to, the name, and the component that will display said page or view, in addition to many other configurations it could have.
If our application has many pages, this array will become a bit complicated to manage.
If you have worked with Nuxt.js, you will know that in its case, by placing components in the pages folder, the route is automatically generated with the component’s name, which we can also place in different folder levels.
Well, that behavior is what we are going to replicate in a more basic way for any Vue application that doesn’t need to use Nuxt.js.
First, we modify router.js; let’s explain it in parts:
We import Vue and Vue router and tell Vue to use it, nothing strange here:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
Making use of Webpack’s dependency management, specifically require.context(), which allows us, in our case, to obtain a context with all the *.vue files in the /pages folder, which is where we store our App’s pages.
This only works in a webpack environment; if we use Vue directly in the browser,
require.contextwill not be defined.
const files = require.context('../pages/', false, /\.vue$/)
const pages = []
files.keys().forEach((key) => {
if (key === './index.js') return
pages[key.replace(/(\.\/|\.vue)/g, '')] = files(key).default
})
Next, we add vue-router routes based on the files we have in our context, using the file name as the path (in lowercase) and for the route name, the name indicated in the component or the file name, and, obviously, the component itself as the component that will manage that route.
const routes = []
Object.keys(pages).forEach(page => {
const route = {
path: '/' + page.toLowerCase()
name: pages[page].name.toLowerCase || page.toLowerCase()
component: pages[page]
}
routes.push(route)
})
Now we just have to instantiate the router:
const router = new Router({
routes: routes
})
And voilà, every component we add to the /pages folder will have its own route without needing to do anything else.
But, what if I want to customize the route, add parameters, or metas? One step further
What we just did is convenient but not very flexible, as we couldn’t configure, for example, a route that contains parameters or specify a meta.requireAuth so that the router, through a guard, checks if the user is logged in.
To solve this, what we can do is define a key called ‘route’ in our component that contains that configuration. If it exists, our route generator will read and apply it; if not, it will use the default procedure.
We modify the route generation part:
const routes = []
Object.keys(pages).forEach(page => {
let route = {}
if (pages[page].route !== undefined) {
route = pages[page].route
} else {
route = {
path: '/' + page.toLowerCase()
}
}
route.component = pages[page]
routes.push(route)
})
And in our component, for example Article.vue, we add the key like this:
<template>
....
</template>
<script>
export default {
....
route: {
path: '/articulo/:slug',
name: 'article',
props: true,
meta: {
auth: true
}
}
...
}
</script>
And so, the route generator we created will use that configuration.
This way, we also have the page configuration within the component itself.
We could keep iterating on this to allow, for example, as Nuxt.js does, creating routes from folders and so on, but I think that is beyond the objective and why not use Nuxt.js directly?
Sergio Carracedo