iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 25
3
Modern Web

忍住不打牌位,只要30天VueJS帶你上A牌系列 第 25

Day25 你要去哪裡? - Vue Router 路由-2

動態路由

有些時候我們需要傳參數到路由中,以動態的方式匹配對的資料及畫面
:userId表示為一個自定義的參數,我們可以透過 this.$route.params.userId 來取得參數的值。

{
  path: 'user/:userId',
  name: 'User',
  component: () => import('@/views/User.vue')
}

今天如果我們希望參數是可以非必填的,就可以使用?來代表optional params

{
  path: '/offers/:member?',
  ...
}

路由模式

vue-router 分為兩種模式:hash(預設)、history
hash mode
ex:http://website.com/#user/id
Vue Router 的默認模式,使用 URL 的 hash 來模擬一個完整的URL,hash 雖然出現在 URL 中,但不會被包含在 HTTP 請求中,對後端完全沒有影響,於是當URL 改變時頁面不會重新加載。

history mode
ex:http://website.com/user/id
history模式少掉了#比較像完整的URL網址,是不是也比較簡單好看,而且這模式利用history.pushState 來完成URL跳轉而無須重新載入頁面,唯一要特別注意到當您的URL匹配不到任何靜態資源時,需配置重新導向到同一個頁面,如index.html。後端配置例子可以看這邊

const router = new VueRouter({
  mode: 'history',
  routes,
  ...
})

巢狀路由

我們來回顧這張圖,巢狀路由是指路由有多階層,所以我們用多層的路由視圖 router-view,且配置好子路由childern
https://ithelp.ithome.com.tw/upload/images/20200923/20129187VHbf5OQ3Ur.png
如圖,我們在app.vue有配置 router-view,然後在Layout.vue再配置一個 router-view 及其子路由Dashboard.vue、User.vue、Setting.vue

{
    path: '/home',
    component: Layout,
    meta: { requireAuth: true },
    childern: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('@/views/Dashboard.vue')
      },
      {
        path: 'user/:userId',
        name: 'User',
        component: () => import('@/views/User.vue')
      },
      {
        path: 'setting',
        name: 'Setting',
        component: () => import('@/views/Setting.vue')
      }
    ]
  }

需注意到,以/開頭的巢狀路由會被當作根目錄符號來運用,children 底下的 path 不加/讓其自動匹配父層路由。

// 不需要再配置路徑'/dashboard','dashboard'即可
childern: [
      {
        path: '/dashboard', // X
        name: 'Dashboard',
        component: () => import('@/views/Dashboard.vue')
      }
    ]

重定向和別名

redirect:
在昨天,我有教大家捕獲所有路由或匹配不到時,則重定向到 404page,今天在加碼教大家一個使用命名(name)來重定向

const router = new VueRouter({
  routes: [
    { path: '/bar', redirect: { name: 'foo' }}
  ]
})

alias:
重定向的意思是,當用戶訪問/a時,URL將會被替換成/b,然後匹配路由為/b。
c 的別名是 d,意味著,當用戶訪問/d時,URL會保持為/d,但是路由匹配則為/c,就像用戶訪問/c一樣,路徑不變,代表一個路由有多個匹配路徑的別名。

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' },
    { path: '/c', component: C, alias: '/d' }
  ]
})

路由守衛

vue-router提供的路由守衛主要用來通過跳轉或取消的方式守衛導航。有多種方式植入路由導航過程中:全局守衛獨享守衛元件內守衛

簡易的路由驗證

首先我們來做個簡易的驗證,還記得我們的元訊息meta: { requireAuth: true }有設定驗證,這樣該路由(包含子路由)在路由改變時就會需要檢查。

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    const token = VueCookies.get('token')
    const isLogin = this.checkLogin(token) // 這裡寫驗證機制(自己客製化)
    if (isLogin) {
      next() // 往下執行
    } else {
      //如果使用者token無效則跳轉到login頁面
      if (to.path !== '/login') {
        next('/login')
      } else {
        next() // 往下執行
      }
    }
  } else {
    next() // 往下執行
  }
})

全局前置守衛

剛剛我們用的beforeEach即是全局前置守衛,其中三個參數tofromnext:
1.to:即將要進入的目標路由物件。
2.from:當前導航即將要離開的路由物件。
3.next:呼叫該方法後,才能進入下一個鉤子函式:

  • next():直接進行下一個鉤子,如果全部鉤子執行完了,則導航的狀態就是confirmed。
  • next(false):中斷當前路由,停留在 from 路由對應的地址
  • next('/routePath'):跳轉指定路由
  • next(error):跳轉錯誤路由,如果傳入next的參數是一個Error實例,則導航會被終止且該錯誤會被傳遞給router.onError()回調。

必須注意,確保所有的邏輯狀態都要調用 next()一次,否則鉤子就不會被解析(resolved)。

全局後置守衛

後置守衛和前置守衛不同的是afterEach不接收第三個參數 next(),也意味著不會改變導航本身。

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

路由獨享的守衛

你可以在路由配置上直接定義beforeEnter守衛,這些守衛與全局前置守衛的方法參數一樣。

const router = new VueRouter({
  routes: [
    {
      path: '/admin',
      component: Admin,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

元件內的守衛

我們可以在component裡面直接定義導航守衛,元件的導航守衛有三種:

  1. beforeRouteEnter:
    此時守衛是不能訪問this,因為守衛在導航確認前被調用,因此此時的新元件還沒被創建。
    不過我們可以透過回調給next來訪問組件實例。在導航被確認的時候執行回調,並且把元件實例作為回調方法的參數。
beforeRouteEnter (to, from, next) {
  next(vm => {
    console.log(vm)
  })
}
  1. beforeRouteUpdate:
    此時守衛已經可以訪問this,所以也就沒有必要透過回調給next,直接是用this即可。
  2. beforeRouteLeave:
    在要離開路由之前,我們是否還能有個猶豫的空間呢?我們可以透過beforeRouteLeave來暫止用戶在還未保存修改前突然離開。
beforeRouteLeave (to, from, next) {
  const answer = window.confirm('您是否離開此頁?您尚有改變未儲存!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

完整的導航解析流程

  1. 導航被觸發。
  2. 在失活的組件裡調用beforeRouteLeave守衛。
  3. 調用全局的beforeEach守衛。
  4. 在重用的組件裡調用beforeRouteUpdate守衛。
  5. 在路由配置裡調用beforeEnter。
  6. 解析異步路由組件。
  7. 在被激活的組件裡調用beforeRouteEnter。
  8. 調用全局的beforeResolve守衛。
  9. 導航被確認。
  10. 調用全局的afterEach鉤子。
  11. 觸發DOM 更新。
  12. 調用beforeRouteEnter守衛中傳給next的回調函數,創建好的組件實例會作為回調函數的參數傳入。

完整路由程式碼:

import Vue from 'vue'
import VueRouter from 'vue-router'
import VueCookies from 'vue-cookies'
import Login from '@/views/Login'
import Layout from '@/views/Layout'

Vue.use(VueRouter)

const routes = [
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    path: '/',
    name: 'redirect',
    redirect: '/home/dashboard'
  },
  {
    path: '/home',
    component: Layout,
    meta: { requireAuth: true },
    childern: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('@/views/Dashboard.vue')
      },
      {
        path: 'user/:userId',
        name: 'User',
        component: () => import('@/views/User.vue')
      },
      {
        path: 'setting',
        name: 'Setting',
        component: () => import('@/views/Setting.vue')
      }
    ]
  },
  {
    path: '/404',
    name: '404',
    component: () => import('@/components/Page404'),
    hidden: true
  },
  {
    path: '*',
    redirect: '/404',
    hidden: true
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    const token = VueCookies.get('token')
    const isLogin = this.checkLogin(token) // 這裡寫驗證機制(自己客製化)
    if (isLogin) {
      next() // 往下執行
    } else {
      //如果使用者token無效則跳轉到login頁面
      if (to.path !== '/login') {
        next('/login')
      } else {
        next() // 往下執行
      }
    }
  } else {
    next() // 往下執行
  }
})

export default router

參考文件:Vue Router

有任何問題歡迎下方留言,如果喜歡我的文章別忘了按讚、訂閱追蹤加分享唷!!
---我是分隔線-----------------------------------------------------------
PollyPO技術-前端設計轉前端工程師-JS踩坑雜記 30 天
喬依司-實作經典 JavaScript 30
五百億-Vue CLI + Firebase 雲端資料庫 30天打造簡易部落格及後臺管理
eien_zheng-前端小嘍嘍的Golang學習旅程_The journey of learning Golang


上一篇
Day24 你要去哪裡? - Vue Router 路由-1
下一篇
Day26 單元測試 unit test
系列文
忍住不打牌位,只要30天VueJS帶你上A牌30

尚未有邦友留言

立即登入留言