Skip to content

Vue Router – Navigation Guards

Cần quản lý, xác minh User trước khi cho truy cập vào trang web ?

Navigation Guard hỗ trợ rất mạnh mẽ việc này. Đồng thời cho phép ta thoải mái xử lý rất nhiều các loại tác vụ trước & sau khi thực hiện navigation (chuyển route)

Table of Contents

    Global

    const router = createRouter({ ... })
    
    
    router.beforeEach((to, from) => {
      // optionally - return false
    })

    Có thể return

    • true / false (dừng navigation)
    • “/path”
    • { name: ‘route_name’, params: {. . .} }

    Async

    router.beforeEach(async (to, from) => {
      // canUserAccess() returns `true` or `false`
      return await canUserAccess(to)
    })

    Hoặc sử dụng next 3rd argument

    // 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()
    })
    
    // GOOD
    router.beforeEach((to, from, next) => {
      if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
      else next()
    })
    Lưu ý là next() chỉ được gọi 1 lần

    Ở trong beforeEach thì next() sẽ không có argument(vm) khác với beforeRouteEnter(local) sẽ có vm argument

    After Hooks

    Thường dùng cho việc phân tích, change page’s title, thông báo v.v.

    router.afterEach((to, from, failure) => {
      if (!failure) sendToAnalytics(to.fullPath)
    })

    Hooks không có next argument & không ảnh hưởng gì đến the navigation

    Before Resolve

    Giống beforeEach triggered mỗi navigation nhưng beforeResolve sẽ được gọi sau khi tất cả guard đã resolved

    Thường sử dụng để fetch data hoặc ~ tác vụ mà tránh làm khi user không thể truy cập page
    router.beforeResolve(async to => {
      if (to.meta.requiresCamera) {
        try {
          await askForCameraPermission()
        } catch (error) {
          if (error instanceof NotAllowedError) {
            // ... handle the error and then cancel the navigation
            return false
          } else {
            // unexpected error, cancel the navigation and pass the error to the global handler
            throw error
          }
        }
      }
    })

    Per-Route Guard (Global setup)

    {
        path: '/users/:id',
    
        beforeEnter(to, from, next) {
          . . .
        }
    }

    Có thể truyền nhiều function cùng lúc

    function removeQueryParams(to, from, next) {
      if (Object.keys(to.query).length)
        return { path: to.path, query: {}, hash: to.hash }
    }
    
    function removeHash(to, from, next) {
      if (to.hash) return { path: to.path, query: to.query, hash: '' }
    }
    
    const routes = [
      {
        path: '/users/:id',
        component: UserDetails,
        beforeEnter: [removeQueryParams, removeHash],
      },
      {
        path: '/about',
        component: UserDetails,
        beforeEnter: [removeQueryParams],
      },
    ]

    Local (In-Component Guards)

    3 phương thức :

    • beforeRouteEnter : gọi trước khi cmp init => không truy cập vào this

    tuy nhiên có thể sử dụng next để truy cập giá trị bên trong cmp sau khi đã được tạo

    beforeRouteEnter (to, from, next) {
      next(vm => {
        // access to component public instance via `vm`
      })
    }
    • beforeRouteUpdate : gọi khi cmp reused => truy cập this

    Ví dụ : route : /users/:id => navigate giữa /users/1/users/2 sẽ được re-used

    • beforeRouteLeave : gọi khi đã navigated => truy cập this
    beforeRouteLeave (to, from) {
      const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
      if (!answer) return false
    }

    Usage :

    const UserDetails = {
      template: `...`,
      beforeRouteEnter(to, from, next) { },
      beforeRouteUpdate(to, from, next) { },
      beforeRouteLeave(to, from, next) { },
    }

    The Full Navigation Resolution Flow

    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 is confirmed.
    10. Call global afterEach hooks.
    11. DOM updates triggered.
    12. Call callbacks passed to next in beforeRouteEnter guards with instantiated instances.

    Re-use Component Case

    Trường hợp này đặc biệt : là vì component nếu re-used thì beforeEnter & beforeRouteEnter không update ^~ 1 khi chúng ta redirectPage

    Vì nó chỉ được kích hoạt 1 lần khi khởi tạo Cmp

    Solution :

    • Sử dụng beforeRouteUpdate kèm beforeRouteEnter
    routes: [
        {
          path: '/',
          name: 'event-list',
          component: EventList,
          props: true // We'll set the page parameter, so we want to send it in as a prop
        }
    ]
    //IN ROUTER.JS
    // Moved the current page & action call outside the component
    function getPageEvents(routeTo, next) {  
      const currentPage = parseInt(routeTo.query.page || 1)
      store
        .dispatch('event/fetchEvents', {
          page: currentPage
        })
        .then(() => {
          // pass it into the component as a prop, so we can print next pages
          routeTo.params.page = currentPage
          next()
        })
    }
    export default {
      props: {
        page: { // current page gets passed in as a prop
          type: Number,
          required: true
        }
      },
      components: {
        EventCard
      },
      beforeRouteEnter(routeTo, routeFrom, next) { // Before we enter the route
        getPageEvents(routeTo, next)
      },
      beforeRouteUpdate(routeTo, routeFrom, next) { // Before we update the route
        getPageEvents(routeTo, next)
      },
      computed: {
        hasNextPage() {
          return this.event.eventsTotal > this.page * this.event.perPage
        }
      }
    }
    
    // IN CMP
    Published inVue Router

    Be First to Comment

    Leave a Reply

    Your email address will not be published. Required fields are marked *