iT邦幫忙

2021 iThome 鐵人賽

DAY 24
1
Modern Web

不只懂 Vue 語法:Vue.js 觀念篇系列 第 24

不只懂 Vue 語法:試解釋如何使用導航守衛?

問題回答

導航守衛(Navigation Guard)可以在 3 個地方使用,包括全域、元件和路由。所謂導航守衛就是在訪問頁面之前,會像一個守衛攔截並執行你所設定的任務,執行後會跳轉或取消訪問該路由。常用情況例如是登入驗證,意思是在用戶載入頁面前,驗證此用戶的 token 是否有效,有就放行,沒有就導回登入頁。另外,導航守衛有不同的 Hook 函式可使用,就如 Vue 也有它的生命週期(createdmounted等等)。但注意,不同的守衛各自都有自己一套 Hook 方法。

注意:Vue 3 是使用 Vue Router 4.x。Vue 2 是使用 Vue Router 3.x。 兩者某些語法會有不同。因此在查看文件時務必留意!

以下主要講解 Vue 3 (Vue Router 4.x)的做法。今天主題比較直白,的確沒有什麼概念要理解,但也會舉出自己曾經使用過的例子作解說。

全域守衛

每次進入任何一個路由前都會作攔截,並執行你在導航守衛裏寫下的任務。這些函式同樣會以非同步方式執行。

執行次序:

  1. router.beforeEach()
    每次進入任何一個路由前,都會作呼叫。

使用情景:身分驗證。

  1. router.beforeResolve()
    beforeEach() 一樣,在進入路由前呼叫。但它會在 beforeEach() 後,並且在所有路由、元件的導航守衛都執行完,最後才會呼叫並執行。

使用情景:呼叫 API,取得遠端資料。

  1. router.afterEach()

使用情景:像 GA 追蹤工具一樣記錄使用者瀏覽紀錄。
結果跳轉路由後才呼叫。

Hook 函式如下:

const router = createRouter({ ... })

router.beforeEach(async (to, from, next) => {
    // to:使用者要跳轉的路由
    // from:使用者前一個訪問的路由
    // 回傳 false 取消跳轉,true / undefined(預設)容許跳轉
    return false
    
    // next 參數在 Vue Router 4 並非必須
})


router.beforeResolve(async (to, from, next) => {
    // do something...
})

router.afterEach((to, from, failure) => {
    // 因為 afterEach 是在路由跳轉結束後才觸發,因此沒有 next
    // failure 參數表示路由跳轉失敗
})

注意:

  • 關於 next 參數,在 Vue Router 3 (即是 Vue 2 )是必須,否則 beforeEach 這個 hook 函式就不會被 resolve 並且報錯。官方例子如下:
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

關於在 Vue Router 3 的 next 參數其他用法,詳情參考官方文件

在 4.x 版本的 Vue Router 文件裏,官方表示有機會移除 next 參數。因此不建議使用。

驗證身份做法示範

這裏簡單示範一下自己使用導航守衛來驗證身份的做法。我當時是透過在 cookies 存放使用者登入時所取得的 token,來證明此使用者已登入。換言之,如果在 cookies 裏沒有 token,或者 token 失效,代表此使用者目前沒登入,並需要跳轉回登入頁。

以下例子,我使用了 js-cookie 插件來操作 cookie。

router / index.js

import Cookies from 'js-cookie';

consr routes = [
    {
        path: '/',
        component: Login // 首頁是登入頁
    },
    ...
]

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

// 使用全域導航守衛
// 每次跳轉頁面都檢查是否有 cookies

router.beforeEach(to => {
    const token = Cookies.get('userToken');
    
    // 登入頁(首頁)不用驗證
    if (to.fullPath === '/') return;
    
    if (!token || !validateToken(token)) {
      // 可能是因為 token 無效,所以需要移除此使用者的 token
      Cookies.remove('userToken');
      return '/';
    }

  }
  // 驗證成功,可以放行
  return true;
});

注意無限循環問題

如果沒有加 if (to.fullPath === '/') return;,就會出現死循環問題。因為跳轉到首頁時,守衛發現首頁也沒有驗證成功,於是再次跳轉到頁面,造成無限循環:

[Vue Router warn]: Detected an infinite redirection in a navigation guard when going from "/" to "/". Aborting to avoid a Stack Overflow. This will break in production if not fixed.
[Vue Router warn]: Unexpected error when starting the router: Error: Infinite redirect in navigation guard at eval

路由獨有守衛

只有在進入某一路由時,才會觸發此導航守衛。 重點如下:

  • 只有 router.beforeEnter() 這 Hook 函式。
  • 只能在 route 物件裏使用以上 Hook 函式。

官方例子:

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

注意:如果該路由的 params、query、hash 有變化,也不會觸發守衛。例如 /user/1/users/2,或者 /user/1/#info/user/2/#detail

遇到要重用守衛的情況,可使用陣列。

官方例子:

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: [removeQueryParams, removeHash],
  },
  {
    path: '/about',
    component: UserDetails,
    beforeEnter: [removeQueryParams],
  },
]

元件守衛

在某元件設定守衛。元件守衛可使用的 Hook 函式:

  1. beforeRouteEnter()
    渲染元件前執行。不能使用 this
  2. beforeRouteUpdate()
    因為元件已掛載了,所以由此開始可以用 this
    路由改變時執行。
  3. beforeRouteLeave()
    離開元件時執行。

寫法與在元件裏使用生命週期一樣:

data() {
    return { ... }
},
beforeRouteEnter(to, from, next) {
    // 無法使用 this
    // 但可以透過 next 使用 this
    next( vm => { ... })
},
beforeRouteUpdate(to, from) {
    // do something...
},
beforeRouteLeave(to, from) {
    // do something...
},

完整執行次序

Vue 官網有提到所有 Hook 的執行次序,在此就不重複了:

截圖至官方文件

總結

  • 導航守衛有 3 種:全域、路由、元件守衛。各自有不同的 Hook 函式。
  • 通常利用全域守衛執行驗證身份。
  • 全域:每次跳轉頁面都會執行守衛。
  • 路由、元件:在單一路由或元件執行守衛。
  • 路由的守衛只能在 route 物件裏設定。

參考資料

重新認識 Vue.js - 4-4 路由守衛(Navigation Guards)
Vue Router - 导航守卫


上一篇
不只懂 Vue 語法:試解釋遞迴元件的用法?
下一篇
不只懂 Vue 語法:試解釋 hash 與 history 模式的分別? 為何 history 模式會回傳 404?
系列文
不只懂 Vue 語法:Vue.js 觀念篇31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言