Vue Router 4–In-Component Guards, Route Meta Fields, and Transitions

John Au-Yeung - Jan 25 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Vue Router 4 is in beta and it’s subject to change.

To build a single page app easily, we got to add routing so that URLs will be mapped to components that are rendered.

In this article, we’ll look at how to use Vue Router 4 with Vue 3.

In-Component Guards

The beforeRouteEnter guard is run before the route that renders the component is confirmed

Therefore, it doesn’t have access to this . 

However, we can access it within the next callback.

To do that, we write:

beforeRouteEnter(to, from, next) {
  next(vm => {
    // ...
  })
}
Enter fullscreen mode Exit fullscreen mode

The vm has the route component’s instance.

The is the only route that supports passing in a callback for next .

We can use this directly in the beforeRouterUpdate and beforeRouterLeave methods, so passing in a callback to next isn’t supported.

So we can just write:

beforeRouteUpdate (to, from, next) {
  this.name = to.params.name
  next()
}
Enter fullscreen mode Exit fullscreen mode

or:

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave?')
  if (answer) {
    next()
  } else {
    next(false)
  }
}
Enter fullscreen mode Exit fullscreen mode

beforeRouteLeave is usually used to prevent the user from accidentally leaving the route.

Navigation Resolution Flow

The steps for navigation are as follows:

  1. Navigation triggered.
  2. Call beforeRouteLeave guards in deactivated components.
  3. Call global beforeEach guards.
  4. Call beforeRouteUpdate guards in reused components.
  5. Call beforeEnter in route configs.
  6. Resolve async route components.
  7. Call beforeRouteEnter in activated components.
  8. Call global beforeResolve guards.
  9. Navigation confirmed.
  10. Call global afterEach hooks.
  11. DOM updates are triggered.
  12. Run callbacks passed to next in beforeRouteEnter guards with instantiated instances.

Route Meta Fields

We can add route meta fields with the meta property.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar</div>"
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar,
          meta: { requiresAuth: true }
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      router.beforeEach((to, from, next) => {
        console.log(to.meta.requiresAuth);
        next();
      });

      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

to add a meta property to our routes array entry.

We have an object with the requiresAuth property.

Then in the beforeEach callback, we can access it with the to.meta.requiresAuth property.

Transitions

We can add transitions for route changes with Vue Router.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
    <style>
      .fade-enter-active,
      .fade-leave-active {
        transition: opacity 0.5s;
      }
      .fade-enter,
      .fade-leave-to {
        opacity: 0;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
      </p>
      <router-view v-slot="{ Component }">
        <transition name="fade">
          <component :is="Component" />
        </transition>
      </router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar</div>"
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

to add transitions to our app.

We add the router-view and access the Component slot prop to get the component that’s displayed through the router-view .

Then we can pass that to the component component inside the router-view .

We set the name prop so that we can set the prefix of the CSS classes we add for the animation.

Between the style tags, we have the styles for various stages of the animation.

The stages of the transitions are listed at https://v3.vuejs.org/guide/transitions-overview.html#class-based-animations-transitions.

Conclusion

We can add in-component guards and route transitions with Vue Router 4.

The way we add route transitions is different from Vue Router 3.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .