Vue.js learning note 13: navigation guard of Vue Router

Posted by blackcow on Sat, 22 Jan 2022 19:22:35 +0100

catalogue

Navigation guard

Global front guard

Global post hook

Route exclusive guard

Guard in assembly

Navigation guard

Let's consider a requirement: how to change the title of a web page in a SPA application?

The page title is displayed through < title >, but SPA only has a fixed HTML. When switching between different pages, the title will not change. However, we can modify the content of < title > through JavaScript.

 window.document.title = 'New title'

So in the Vue project, where is it modified? When is it appropriate to modify it?

Common modification methods:

It is easy for us to think of modifying the location of the title is the corresponding component of each route vue file. The periodic function can be declared by mounted, and the corresponding code can be modified.

However, when there are many pages, this method is not easy to maintain (because similar code needs to be executed on multiple pages).

Is there a better way? Just use the navigation guard.

What is a navigation guard?

The navigation guard provided by Vue Router is mainly used to monitor the entry and exit of routes.

Vue Router provides hook functions of before each and after each, which will be triggered before and after the route changes.

Global front guard

You can use router Before each registers a global front guard:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

When a navigation is triggered, the global front guard is called according to the creation order. The guard is executed asynchronously. At this time, the navigation is waiting until all guards are resolve d.

Each guard method receives three parameters:

  • to: Route: the route object to be entered.

  • from: Route: the route object that the current navigation is about to leave.

  • next: Function: this method must be called to resolve this hook. The execution effect depends on the call parameters of the next method.

    • next(): the next hook in the pipeline. If all hooks are executed, the status of navigation is confirmed.

    • next(false): interrupt the current navigation. If the URL of the browser is changed (either manually by the user or the browser Back button), the URL address will be reset to the address corresponding to the from route.

    • next('/') or next({path: '/'}): jump to a different address. The current navigation is interrupted and a new navigation is performed. You can pass any location object to the next, and you are allowed to set options such as replace: true, name: 'home' and any Route link to prop or router.push Options in.

    • next(error): (2.4.0 +) if the parameter passed into next is an Error instance, the navigation will be terminated and the Error will be passed to router.onError() Registered callback.

Ensure that the next function is strictly called once in any given navigation guard. It can occur more than once, but only when all logical paths do not overlap, otherwise the hook will never be parsed or reported an error.

Here we use beforeEach to modify the title.

First, we can define some titles in the hook, which can be defined by meta (routing meta information).

const routes = [	
     {
        path: '/home',  
		name: 'Home',
    	component: Home,	
		meta: {
			title: 'home page'
		}
      },
      {
        path: '/about',
        name: 'About',
        component: About,
            meta: {
                title: 'about'
            }
      }
]

Secondly, use the navigation guard to modify our title.

router.beforeEach((to, from, next) => {
  	window.document.title = to.meta.title
	next()
})

Here is another example of redirecting to / login when the user is not authenticated (not logged in or expired):

router.beforeEach((to, from, next) => {
   if (to.name !== 'Login' && !isAuthenticated) {
       next({name: 'Login'})
   } else {
       next()
   }
 })

Global post hook

You can also use afterEach to register the global post hook, but you don't need to actively call the next() function.

router.afterEach((to, from) => {
  // ...
})

// Modify our title
router.afterEach((to) => {
  window.document.title = to.meta.title
})

Route exclusive guard

You can directly define the beforeEnter guard on the route configuration. It will be called only when entering this route. The method parameters of these guards are the same as those of the global front guard.

const routes = [    
      {
         path: '/home',  
         name: 'Home',
         component: Home,    
         meta: {
             title: 'home page'
         },
         beforeEnter: (to, from, next) => {
           // ...
         }
       },
       {
         path: '/about',
         name: 'About',
         component: About,
             meta: {
                 title: 'about'
             }
       }
 ]

Guard in assembly

You can directly define the following routing navigation guards in the routing component:

  • beforeRouteEnter

  • beforeRouteUpdate

  • beforeRouteLeave

 <script>
     export default {
       name: 'Goods'
       beforeRouteEnter(to, from, next) {
         // Before rendering the component, the corresponding route is called before confirm.
         // no Yes! Get component instance ` this`
         // Because the component instance has not been created before the guard is executed
       },
       beforeRouteUpdate(to, from, next) {
         // Called when the current route changes but the component is reused
         // For example, for a path / goods/:id with dynamic parameters, when jumping between / goods/1 and / goods/2,
             // Since the same Goods component will be rendered, the component instance will be reused. The hook will be called in this case.
         // Can access component instance ` this`
       },
       beforeRouteLeave(to, from, next) {
         // Called when the navigation leaves the corresponding route of the component
         // Can access component instance ` this`
       }
     }
 </script>

These three guards are written in the component. The beforeRouteEnter guard cannot access this because the guard is called before the navigation confirmation, so the incoming new component has not been created.

However, you can access the component instance by passing a callback to next. The callback is executed when the navigation is confirmed, and the component instance is used as the parameter of the callback method. The other two can use this.

beforeRouteEnter (to, from, next) {
   next(vm => {
     // Accessing component instances through 'vm'
   })
 }

Note that beforeRouteEnter is the only guard that supports passing callbacks to the next. For beforeRouteUpdate and beforeRouteLeave, this is already available, so callback delivery is not supported because it is not necessary.

 beforeRouteUpdate (to, from, next) {
   // just use `this`
   this.name = to.params.name
   next()
 }

This exit guard is usually used to prevent users from leaving suddenly before saving their changes. The navigation can be cancelled by next(false).

beforeRouteLeave (to, from, next) {
   const answer = window.confirm('There are unsaved changes. Do you really want to leave?')
   if (answer) {
     next()
   } else {
     next(false)
   }
 }

Topics: Javascript Vue