Importación
Para la navegación en Vue utilizamos el paquete vue-router. Podemos ver la documentación oficial con todos los detalles aquí.
Debemos importarlo en nuestro main.js:
import router from './router';
Y también en la parte delnew Vue:
new Vue({
router,
render: h => h(App),
}).$mount('#app')
Definir rutas
Para definir las rutas, tendremos un directorio router/ dentro de src/, que contendrá index.js. En él podemos definir una lista con un objeto por cada ruta, de la siguiente forma:
{
path: '/frameworkFundeweb'
name: 'framework fundeweb'
component: FrameworkFundeweb
}
Aquí nos definimos la ruta que tendremos en la url, un nombre que incluiremos opcionalmente, para referenciarlo desde otras partes del código en lugar de escribir la ruta, y por último el componente que se mostrará cuando naveguemos a la ruta.
Un ejemplo de fichero completo sería este:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Profile from '../views/Profile.vue'
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "home",
component: Home
},
{
path: '/profile',
name: 'profile',
component: Profile,
props: true
}
]
const router = new VueRouter({
mode: 'history',
routes
})
export default router
Si nos fijamos, podemos poner en cada ruta un campo props a true. Con esto, cuando naveguemos a esa ruta, los parámetros que se le pasen cuyo nombre coincida con un prop definido en el componente se asignará a este directamente. Es importante tener en cuenta que para hacer esto, tendremos que hacer la navegación utilizando el name, y no el path, pues si no no funciona.
Definir sub-rutas
Podemos definir rutas hijas de una ruta ya existente, añadiéndolas al parámetro children de la ruta padre:
{
path: "/about",
name: "about",
component: About,
children: [{path: "detalle", name: "Detalle", component: Detalle }]
},
En este ejemplo la ruta padre sería /about, y la ruta hija /about/detalle. A la hora de definir rutas hijas, es importante tener en cuenta que el path no debe incluir la barra ("/"), pues si lo hace lo toma como lo que hay inmediatamente después de la url de nuestro sitio.
Añadir rutas dinámicamente
Podemos añadir rutas en tiempo de ejecución, con la instrucción addRoute que nos proporciona el propio router. Para ello, en archivos JavaScript tendremos que importar el router, y si es desde nuestros componentes podemos acceder directamente a través de this.$router. La función addRoute tiene un primer parámetro opcional en el que le debemos indicar el nombre de la ruta padre si queremos que sea una sub-ruta, y el otro parámetro será el objeto de la ruta:
// Ruta normal
this.$router.addRoute({ path: "/algo", name: "Algo", component: Algo });
// Sub ruta
this.$router.addRoute("NombrePadre", { path: "algo", name: "Algo", component: Algo });
Podemos encontrar este método, así como el resto de métodos que ofrece Vue Router en su documentación API Reference.
Un detalle a tener en cuenta es que si redirigimos a un sitio externo, las rutas añadidas dinámicamente no son persistentes, cuando volvamos esas rutas no existirán, habría que añadirlas de nuevo.
Vista del router
El router habrá que incluirlo como un componente más. Así, podemos definir la parte de la pantalla que cambiará cuando se cambie de ruta, pudiendo dejar fijo, por ejemplo, un menú lateral, y que sólo se tenga que cargar el contenido de la página.
Para esto, en el archivo .vue inicial, el que se carga al principio, basta con incluir:
<router-view></router-view>
Así, esta parte de la página se sustituirá por el componente correspondiente, cambiando sólo la parte necesaria de la página sin tener que volver a cargarlo todo.
Navegar
Para relizar la navegación, podríamos hacerlo conhref, pero es mejor utilizar router-link:
<router-link :to="{ name: 'nuevaComunicacion', params: { idReenviar: fila.data.id }}">
Como vemos, se le indica a dónde navegar con el nombre que hemos definido previamente para la ruta. Además podemos pasar datos como parámetro, con params. Después, en el componente al que se navega, podemos coger dichos datos referenciando a this.$route.params. En este ejemplo, el objeto que se ha pasado se cogería con this.$route.params.idReenviar.
Si queremos hacerlo de forma programática, en la parte de JavaScript con this.$router.push():
// path
this.$router.push('home')
// path
this.$router.push({ path: 'home' })
// nombre + parámetros path (....um.es/loquesea?userId=123)
this.$router.push({ name: 'user', params: { userId: '123' } })
// nombre + parámetros query (....um.es/loquesea?userId=123)
this.$router.push({ name: 'user', params: { userId: '123' } })
Aquí vemos las opciones de path params y query params. Para utilizar los path params, es necesario navegar por nombre, no utilizando el path. Para obtener sus valores en el componente al que se navega, lo podemos hacer con:
// path params this.$route.params.nombreparametro // query params this.$route.query.nombreparametro
Es importante saber que estos valores se reciben como un string.
Interceptores de navegación
Vue router ofrece la posibilidad de añadir código para ejecutarlo cuando se produzca la navegación, con los navigation guards. Son una serie de métodos que podemos definir. El proceso de navegación, y el momento en el que se ejecuta cada método es el siguiente:
La navegación es activada.
Se llama a todos los
beforeRouterLeaveque se hayan registrado y que no van a ser reutilizados en la siguiente ruta a la que voy.Se llama a todos los interceptores globales
beforeEach.Se llama a todos los
beforeRouteUpdatede los componentes que van a ser reutilizados en la siguiente ruta a la que voy.Se llama a
beforeEnterque hemos configurado en la ruta a la que voy.Se resuelven toda la asincronía de componentes de esa ruta.
Se llama a
beforeRouteEnterde los componentes que van a estar activos.Se llama a los interceptores globales
beforeResolve.Se da la navegación como confirmada.
Se llama al interceptor
afterEach.Se llama a los callbacks pasados a next in
beforeRouteEnter.Y vuelta a empezar cuando se lanza una nueva navegación.
Estos métodos pueden ser globales o locales a cada ruta.
Interceptores globales
beforeEach
Podemos registrar interceptores de manera global para definir un comportamiento que se ejecute justo antes de producirse la navegación a cualquier ruta. Esto lo hacemos conrouter.beforeEach, de esta manera:
router.beforeEach((to, from, next) => {
// ...
})
Los parámetros de esta función son:
to: objeto con la información de la ruta a la que se navega.
from: objeto con la información de la ruta desde la que se navega.
next: es una función que permite reanudar la navegación. Nos permite llamarla de diferentes maneras para tener diferentes comportamientos:
next()reanuda la navegación hacia la ruta indicada ento.next('/')redirige a la ruta que se le pase como parámetro.next(false)aborta la navegación.next(new Error())aborta la navegación y se ejecuta el callbackonError.
Hay que asegurarse de que sólo se llama anext()una vez, en caso contrario podemos encontrarnos errores. Por ejemplo, esto estaría mal:
// BAD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
// if the user is not authenticated, `next` is called twice
next()
})
La forma correcta sería esta:
// GOOD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
Se pueden registrar múltiples interceptores del mismo tipo, y estos se ejecutarán en orden de aparición, es decir, el primero en registrarse es el primero que se ejecuta.
Los interceptores nos pueden venir bien para comprobar algún estado global de la aplicación, como la autorización del usuario.
beforeResolve
Se pueden registrar estos interceptores con router.beforeResolve, de la misma forma que los beforeEach. La diferencia es que estos se ejecutan justo antes de que se confirme la navegación, después de haber ejecutado los interceptores de ruta y de componentes, y que los componentes de ruta asíncronos se resuelvan.
afterEach
También se pueden registrar globalmente interceptoresafterEachque se ejecutarán después de confirmar la navegación. Se definen como losbeforeEach, pero sin un parámetronext, pues ya se ha confirmado la navegación:
router.afterEach((to, from) => {
// ...
})
Interceptores por ruta
Se pueden definir interceptoresbeforeEnterdirectamente en el objeto que define las rutas, con la misma notación que los interceptores globales before:
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
Interceptores por componente
Se pueden definir interceptores de navegación dentro de los componentes que se pasan a las rutas, con las siguientes opciones:
beforeRouteEnter: se llama antes de que la ruta que navega a este componente se confirme. No tiene acceso althisdel componente, pues todavía no se ha creado.beforeRouteUpdate: se llama antes de que la ruta que lleva a este componente haya cambiado. Por ejemplo, para una ruta con parámetros dinámicos/foo/:id, cuando navegamos entre/foo/1y/foo/2, se reutiliza el mismo componente, y este interceptor se ejecuta cuando eso ocurre. Éste sí tiene acceso athis.beforeRouteLeave: se ejecuta cuando la ruta que renderiza este componente va a navegar a otra ruta. También tiene acceso athis.
beforeRouteEnter (to, from, next) {
// called before the route that renders this component is confirmed.
// does NOT have access to `this` component instance,
// because it has not been created yet when this guard is called!
},
beforeRouteUpdate (to, from, next) {
// called when the route that renders this component has changed.
// This component being reused (by using an explicit `key`) in the new route or not doesn't change anything.
// For example, for a route with dynamic params `/foo/:id`, when we
// navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
// will be reused (unless you provided a `key` to `<router-view>`), and this hook will be called when that happens.
// has access to `this` component instance.
},
beforeRouteLeave (to, from, next) {
// called when the route that renders this component is about to
// be navigated away from.
// has access to `this` component instance.
}
Volver arriba tras navegar
Por defecto, al navegar a otra ruta, se mantiene el scroll que teníamos en la ruta anterior. Si queremos que al hacerlo la página se ponga de nuevo al inicio, arriba del todo, tenemos que definir así el scrollBehavior al crear la instancia del router:
const router = new VueRouter({
...,
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 };
}
})
Podemos consultar la documentación oficial para más detalles acerca del scrollBehavior.