Trong thế giới phát triển web hiện nay, việc quản lý điều hướng trở nên ngày càng quan trọng. Trang web Cafedev hôm nay muốn chia sẻ với bạn về cách sử dụng các bộ lọc điều hướng trong Vue Router. Với các bộ lọc này, bạn có thể kiểm soát việc điều hướng của ứng dụng VueJS của mình một cách linh hoạt và mạnh mẽ. Hãy cùng khám phá những điều thú vị về Router VueJS với các bộ lọc điều hướng!


Như tên gọi, các bộ lọc điều hướng được cung cấp bởi Vue Router chủ yếu được sử dụng để bảo vệ các điều hướng bằng cách chuyển hướng hoặc hủy bỏ nó. Có một số cách để kết nối vào quá trình điều hướng địa chỉ: toàn cầu, theo từng địa chỉ, hoặc trong các thành phần.

1. Các Bộ Lọc Toàn Cầu Trước

Bạn có thể đăng ký các bộ lọc toàn cầu trước bằng cách sử dụng router.beforeEach:

const router = createRouter({ ... })

router.beforeEach((to, from) => {
  // ...
  // explicitly return false to cancel the navigation
  return false
})

Các bộ lọc toàn cầu trước được gọi theo thứ tự tạo, mỗi khi một cuộc điều hướng được kích hoạt. Các bộ lọc có thể được giải quyết bất đồng bộ, và cuộc điều hướng được coi là đang chờ cho đến khi tất cả các khớp đã được giải quyết.
Mỗi hàm bộ lọc nhận hai đối số:

  • to: địa chỉ địa điểm đích trong định dạng chuẩn hóa đang được điều hướng tới.
  • from: địa chỉ địa điểm hiện tại trong định dạng chuẩn hóa đang được rời đi.
    Và có thể trả về tùy chọn sau:
  • false: hủy bỏ điều hướng hiện tại. Nếu URL của trình duyệt được thay đổi (hoặc bằng cách thủ công bởi người dùng hoặc thông qua nút back), nó sẽ được đặt lại thành của địa chỉ from.
  • Một Địa điểm Địa chỉ: Chuyển hướng đến một vị trí khác bằng cách truyền một vị trí địa chỉ như bạn đang gọi router.push(), cho phép bạn truyền các tùy chọn như replace: true hoặc name: 'home'. Cuộc điều hướng hiện tại sẽ bị loại bỏ và một cuộc điều hướng mới được tạo với cùng from.
router.beforeEach(async (to, from) => {
  if (
    // make sure the user is authenticated
    !isAuthenticated &&
    // ❗️ Avoid an infinite redirect
    to.name !== 'Login'
  ) {
    // redirect the user to the login page
    return { name: 'Login' }
  }
})

  • Cũng có thể ném một Lỗi nếu gặp tình huống không mong muốn. Điều này cũng sẽ hủy bỏ điều hướng và gọi bất kỳ hồi đáp nào đã được đăng ký qua router.onError().
    Nếu không có gì, undefined hoặc true được trả về, điều hướng được xác nhận, và bộ lọc điều hướng kế tiếp được gọi.

Tất cả các điều trên hoạt động giống nhau với các hàm async và Promises:

router.beforeEach(async (to, from) => {
  // canUserAccess() returns `true` or `false`
  const canAccess = await canUserAccess(to)
  if (!canAccess) return '/login'
})

Tham số thứ ba tùy chọn next

Trong các phiên bản trước của Vue Router, cũng có thể sử dụng một tham số thứ ba next, điều này là một nguồn lỗi phổ biến và đã được đi qua một RFC để loại bỏ nó. Tuy nhiên, nó vẫn được hỗ trợ, có nghĩa là bạn có thể truyền một tham số thứ ba vào bất kỳ bộ lọc điều hướng nào. Trong trường hợp đó, bạn phải gọi next chính xác một lần trong mọi lần đi qua một bộ lọc điều hướng. Nó có thể xuất hiện nhiều lần, nhưng chỉ nếu các đường dẫn logic không chồng chéo, nếu không, hook sẽ không bao giờ được giải quyết hoặc tạo ra lỗi. Dưới đây là một ví dụ xấu về chuyển hướng người dùng đến /login nếu họ chưa được xác thực:

// 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()
})

Đây là phiên bản đúng:

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

2. Bộ Lọc Toàn Cầu Trước Khi Giải Quyết

Bạn có thể đăng ký một bộ lọc toàn cầu với router.beforeResolve. Điều này tương tự như router.beforeEach vì nó kích hoạt trên mọi điều hướng, nhưng các bộ lọc giải quyết được gọi ngay trước khi điều hướng được xác nhận, sau khi tất cả các bộ lọc trong thành phần và các thành phần đường dẫn không đồng bộ đã được giải quyết. Dưới đây là một ví dụ đảm bảo rằng người dùng đã cung cấp quyền truy cập vào Camera cho các địa chỉ đường dẫn có thuộc tính tùy chỉnh requiresCamera:

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
      }
    }
  }
})

router.beforeResolve là điểm lý tưởng để lấy dữ liệu hoặc thực hiện bất kỳ hoạt động nào khác mà bạn muốn tránh nếu người dùng không thể vào trang.

3. Hooks Toàn Cầu Sau

Bạn cũng có thể đăng ký các hook toàn cầu sau, tuy nhiên, khác với các bộ lọc, các hook này không có chức năng next và không thể ảnh hưởng đến điều hướng:

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

Chúng hữu ích cho phân tích, thay đổi tiêu đề của trang, các tính năng về khả năng truy cập như thông báo về trang và nhiều điều khác.
Chúng cũng phản ánh các lỗi điều hướng như là đối số thứ ba:

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

Tìm hiểu thêm về các lỗi điều hướng trên hướng dẫn của nó.

4. Tiêm vào toàn cầu trong các bộ lọc

Từ Vue 3.3, bạn có thể sử dụng inject() trong các bộ lọc điều hướng. Điều này hữu ích để tiêm các thuộc tính toàn cầu như các cửa hàng pinia. Bất cứ điều gì được cung cấp bằng app.provide() cũng có thể truy cập trong router.beforeEach(), router.beforeResolve(), router.afterEach():

// main.ts
const app = createApp(App)
app.provide('global', 'hello injections')

// router.ts or main.ts
router.beforeEach((to, from) => {
  const global = inject('global') // 'hello injections'
  // a pinia store
  const userStore = useAuthStore()
  // ...
})

5. Bộ Lọc Theo Từng Đường Dẫn

Bạn có thể xác định các bộ lọc beforeEnter trực tiếp trên đối tượng cấu hình của một đường dẫn:

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from) => {
      // reject the navigation
      return false
    },
  },
]

Các bộ lọc beforeEnter chỉ kích hoạt khi nhập vào đường dẫn, chúng không kích hoạt khi params, query hoặc hash thay đổi ví dụ như chuyển từ /users/2 sang /users/3 hoặc từ /users/2#info sang /users/2#projects. Chúng chỉ được kích hoạt khi điều hướng từ một đường dẫn khác.
Bạn cũng có thể truyền một mảng các hàm vào beforeEnter, điều này hữu ích khi tái sử dụng các bộ lọc cho các đường dẫn khác nhau:

function removeQueryParams(to) {
  if (Object.keys(to.query).length)
    return { path: to.path, query: {}, hash: to.hash }
}

function removeHash(to) {
  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],
  },
]

Khi làm việc với đường dẫn lồng nhau, cả các đường dẫn cha và con đều có thể sử dụng beforeEnter. Khi đặt trên một đường dẫn cha, nó sẽ không được kích hoạt khi di chuyển giữa các con có cùng cha. Ví dụ:

const routes = [
  {
    path: '/user',
    beforeEnter() {
      // ...
    },
    children: [
      { path: 'list', component: UserList },
      { path: 'details', component: UserDetails },
    ],
  },
]

beforeEnter trong ví dụ trên sẽ không được gọi khi di chuyển giữa /user/list/user/details, vì chúng chia sẻ cùng một cha. Nếu chúng ta đặt bộ lọc beforeEnter trực tiếp trên đường dẫn details thay vào đó, nó sẽ được gọi khi di chuyển giữa hai đường dẫn đó.
tip Có thể đạt được hành vi tương tự với các bộ lọc theo từng đường dẫn bằng cách sử dụng trường meta của đường dẫn và các bộ lọc điều hướng toàn cầu.

6. Bộ Lọc Trong Các Thành Phần

Cuối cùng, bạn có thể xác định trực tiếp các bộ lọc điều hướng đường dẫn trong các thành phần đường dẫn (những cái được truyền vào cấu hình của router)

Sử Dụng API Tùy Chọn

Bạn có thể thêm các tùy chọn sau vào các thành phần đường dẫn:
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave

<script>
export default {
  beforeRouteEnter(to, from) {
    // 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) {
    // called when the route that renders this component has changed, but this component is reused in the new route.
    // For example, given a route with params `/users/:id`, when we navigate between `/users/1` and `/users/2`,
    // the same `UserDetails` component instance will be reused, and this hook will be called when that happens.
    // Because the component is mounted while this happens, the navigation guard has access to `this` component instance.
  },
  beforeRouteLeave(to, from) {
    // called when the route that renders this component is about to be navigated away from.
    // As with `beforeRouteUpdate`, it has access to `this` component instance.
  },
}
</script>

Bộ lọc beforeRouteEnter KHÔNG có quyền truy cập vào this, vì bộ lọc được gọi trước khi điều hướng được xác nhận, do đó thành phần mới sẽ chưa được tạo ra.
Tuy nhiên, bạn có thể truy cập vào thể hiện bằng cách truyền một callback vào next. Callback sẽ được gọi khi điều hướng được xác nhận, và thể hiện của thành phần sẽ được truyền vào callback như là đối số:

beforeRouteEnter (to, from, next) {
  next(vm => {
    // access to component public instance via `vm`
  })
}

Lưu ý rằng beforeRouteEnter là duy nhất hỗ trợ việc truyền một callback vào next. Đối với beforeRouteUpdatebeforeRouteLeave, this đã có sẵn, vì vậy việc truyền một callback là không cần thiết và do đó không được hỗ trợ:

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

Bộ lọc rời thường được sử dụng để ngăn người dùng thoát khỏi đường dẫn một cách vô tình với các chỉnh sửa chưa được lưu. Việc điều hướng có thể bị hủy bỏ bằng cách trả về false.

beforeRouteLeave (to, from) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (!answer) return false
}

Sử Dụng API Hợp Thức

Nếu bạn viết thành phần của mình bằng cách sử dụng API Hợp Thức, bạn có thể thêm bộ lọc cập nhật và rời bằng cách sử dụng onBeforeRouteUpdateonBeforeRouteLeave tương ứng. Vui lòng tham khảo phần API Hợp Thức để biết thêm chi tiết.

7. Toàn Bộ Quy Trình Giải Quyết Điều Hướng

  1. Điều hướng được kích hoạt.
  2. Gọi các hàm bảo vệ beforeRouteLeave trong các component bị hủy kích hoạt.
  3. Gọi các hàm bảo vệ global beforeEach.
  4. Gọi các hàm bảo vệ beforeRouteUpdate trong các component được tái sử dụng.
  5. Gọi beforeEnter trong cấu hình route.
  6. Giải quyết các component route không đồng bộ.
  7. Gọi beforeRouteEnter trong các component được kích hoạt.
  8. Gọi các hàm bảo vệ global beforeResolve.
  9. Điều hướng được xác nhận.
  10. Gọi các hook global afterEach.
  11. Cập nhật DOM được kích hoạt.
  12. Gọi các callback truyền vào next trong các hàm bảo vệ beforeRouteEnter với các instance đã được khởi tạo.

Trên trang web Cafedev, chúng ta đã khám phá về cách sử dụng các bộ lọc điều hướng trong Vue Router để kiểm soát việc điều hướng trong ứng dụng VueJS. Việc hiểu và sử dụng hiệu quả các navigation guards không chỉ giúp bạn tăng tính linh hoạt mà còn nâng cao trải nghiệm người dùng. Hy vọng rằng thông tin mà chúng tôi chia sẻ đã giúp bạn có cái nhìn rõ ràng hơn về cách làm việc với Router VueJS và navigation guards. Hãy tiếp tục khám phá và phát triển các ứng dụng tuyệt vời trên nền tảng VueJS!

Tham khảo thêm: MIỄN PHÍ 100% | Series tự học Vuejs từ cơ bản tới nâng cao

Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!