Phân quyền Router trong VueJS với Vue-Router
Nguồn:
https://router.vuejs.org/guide/advanced/navigation-guards.html
Giới thiệu
Chào các bạn, Hôm nay mình xin được chia sẻ với các bạn cách phân quyền router trong VueJS với Vue-Router4 .Trong bài viết này mình sẽ tập trung vào hướng xử lý logic ngay tại VueRouter bỏ qua khái niệm và định nghĩa cơ bản vuerouter, dữ liệu để định nghĩa phân quyền sẽ được các bạn linh hoạt áp dụng vào từng bài tập, dự án nhé. Chính vì vậy, mình sẽ tổ chức dữ liệu mẫu ở dưới ngay trong VueJS để thực hiện.
Về cơ bản, phân quyền trong VueJS là cách các bạn tác động tới dữ liệu phân quyền trước khi router được xác định chuyển hướng tới đích. Vậy làm sao để tác động được vào thời điểm này? Chúng ta sẽ sử dụng một khái niệm, đó chính là Navigation Guards trong VueJS. Navigation Guards được chia làm 3 phần chính : globally , per-route, or in-component. 3 phần này sẽ quyết định rằng việc các bạn phân quyền sẽ được định nghĩa ở đâu:
- Globally Guard - Can thiệp vào toàn bộ router
- Per-Route Guard - Can thiệp vào từng router
- In-Component Guards - Can thiệp vào từng component trong vuejs
Áp dụng
Globally Guard - Can thiệp vào toàn bộ router
Với Global Guards, các bạn sẽ can thiệp vào toàn bộ các router đã được định nghĩa trong VueRouter. Đồng thời các bạn sẽ có 3 hook tại 3 thời điểm có thể can thiệp vào dữ liệu, chuyển hướng theo thứ tự là :
- beforeEach (Global Before Guards) : Được gọi tới khi bất kì router nào của bạn được người dùng kích hoạt . Ở đây nhận 2 tham số là :
- to : chứa thuộc tính router người dùng hướng đến
- from : chứa thuộc tính router hiện tại chuyển hướng đi
Trong phần này mình sẽ nêu ra hướng xử lý logic mình đã làm trong dự án : - Với app có ít role mình có thể điều hướng ngay tại đây mà không cần xử lý bên trong.
router.beforeEach(async (to) => {
// this app uses JWT so I use a token to sign in app .
const token = window.$cookies.get(generateStorageKey(APP_TOKEN_NAME));
// Because you can't access the store here. .So after the login , you need save user information and permission in localStorage or the backend needs to create an API to fetch permissions.
const userInfo = JSON.parse(localStorage.getItem('userInfo'));
// check token + role here
return checkRouterWithTokenAndRole(token, to, userInfo);
});
- Với app có nhiều role : Lấy ra danh sách role ở phần này , lưu vào trong store, dùng role này để kiểm tra cho từng pre-router ,...
router.beforeEach(async (to) => {
// Saving role in store and check in pre-router
setApiRoleStore();
return checkRouterWithToken(token, to, userInfo);
});
- beforeResolve (Global Resolve Guards) Được gọi ngay sau khi xử lý beforeEach và trước khi điều hướng được xác nhận . Đây là nơi lý tưởng để lấy dữ liệu hoặc thực hiện bất kỳ thao tác nào khác mà bạn muốn tránh thực hiện nếu người dùng không thể vào trang .
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
}
}
}
})
- afterEach (Global After Hooks) Được gọi khi mà tất cả các logic điều hướng của chúng ta đã xong. Ở afterEach, chúng ta chỉ có 2 tham số là to và from chính vì vậy mà không thể điều hướng sang một router khác ở thời điểm này. Mặc dù có thể dùng router push để chuyển sang một chu kì router khác nhưng về mặt ý nghĩa afterEach không phải để điều hướng . Một số logic có thể xử lý ở đây : phân tích, thay đổi tiêu đề trang, thay đổi menu , các tính năng trợ năng như thông báo trang và nhiều tính năng khác .
router.afterEach((to, from, failure) => {
changeMenuWidthPermission();
if (failure) {
/** check error router if it isn't duplicate **/
if (!isNavigationFailure(failure, NavigationFailureType.duplicated)) {
console.log(failure);
}
}
});
Per-Route Guard – Can thiệp vào từng Router
Nếu như Global Guard viết một nơi can thiệp cho toàn bộ Router, thì Per-Route Guard lại chỉ can thiệp vào cụ thể Router chúng ta định nghĩa:
- beforeEnter chỉ kích hoạt trước khi router được chuyển hướng
- beforeEnter được xử lý sau beforeEach và trước afterEach
- Khi làm việc với các tuyến đường lồng nhau , cả tuyến cha và tuyến con đều có thể sử dụng beforeEnter. Khi được đặt trên tuyến cha, nó sẽ không được kích hoạt khi di chuyển giữa các tuyến con có cùng tuyến cha đó
const routes = [
{
path: '/user',
beforeEnter() {
// ...
},
children: [
{ path: 'list', component: UserList },
{ path: 'details', component: UserDetails }
]
}
]
- Phần này là phần tốt nhất để can thiệp vào các router nếu có nhiều permission khác nhau để điều hướng . Nhưng nếu có ít logic và có ít quyền các bạn có thể xử lý trong beforeEach và bỏ qua phần này
createRoute(path, nameKey, type, roleScreen, component) {
return {
path: path,
name: nameKey,
meta: { type: type },
beforeEnter: () => {
const apiStore = useApiStore();
// using the Role in store is saved in beforeEach from earlier.
return !roleScreen || apiStore.allRole.has(roleScreen) ? true : { path: '/notFound' };
},
component: component
};
}
Component Guards – Can thiệp vào từng component trong VueJS
Đây là mức độ nhỏ nhất cho việc các bạn can thiệp vào Router – đó là Component. Chúng ta sẽ có Options cần lưu ý ở đây:
createRoute(path, nameKey, type, roleScreen, component) {
return {
path: path,
name: nameKey,
meta: { type: type },
beforeEnter: () => {},
beforeRouterEnter: () => {},
beforeRouterUpdate: () => {},
beforeRouterLeave: () => {},
component: component
};
}
- 3 hook này sử dụng cùng chỗ với beforeEnter và được gọi ở các thời điểm khác nhau:
- beforeRouteEnter: Được gọi trước khi mà điều hướng được xác nhận. Ở đây bạn không thể truy cập vào con trỏ this của Vue, vì nó được chạy trước khi mà component được khởi tạo. Đây là guard duy nhất hỗ trợ việc truyền lệnh gọi lại tới next . Đối với beforeRouteUpdate và beforeRouteLeave, this đã có sẵn, do đó việc truyền lệnh gọi lại là không cần thiết và do đó không được hỗ trợ
- beforeRouteUpdate: Được gọi khi mà router đã được thay đổi và chúng ta sẽ dụng lại Component chúng ta định nghĩa ra beforeRouteUpdate.
- beforeRouteLeave: Được gọi trước khi mà chúng ra chuyển hướng ra khỏi component tới một Route khác. Ở mục này có thể sử dụng để ngăn người dùng với những chỉnh sửa chưa được lưu .
beforeRouterLeave: (to,from) => {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (!answer) { return false }
},
Kết bài :
Việc sử dụng phân quyền trong router có thể xử lý ở nhiều cấp độ khác nhau :
- Việc điều hướng router, lấy dữ liệu , xử lý logic khi người dùng không thể vào trang
- Globally Guard : beforeEach , beforeResolve
- Per-Route Guard : beforeEnter ,
- Component Guards : beforeRouteEnter .
- Việc update logic phân tích, thay đổi tiêu đề trang, thay đổi menu , các tính năng trợ năng như thông báo trang và nhiều tính năng khác
- Globally Guard : afterEach ,
- Component Guards : beforeRouteLeave .
All rights reserved