Vue 導航守衛(路由的生命週期)

YXi發表於2019-08-13

"導航" 表示路由正在發生改變

vue-router 提供的導航守衛主要用來通過跳轉或取消的方式守衛導航。有多種機會植入路由導航過程中,他們分為三種:

  • 全域性的
    • router.beforeEach
    • router.beforeResolve
    • router.afterEach
  • 單個路由獨享的
    • beforeEnter
  • 元件級的
    • beforeRouteEnter
    • beforeRouteUpdate
    • beforeRouteLeave

注:引數或查詢的改變並不會觸發進入/離開的導航守衛 (你可以通過觀察 $route 物件來應對這些變化,或使用 beforeRouteUpdate 的元件內守衛。)

給一張圖直觀的看一下:

圖片載入失敗!

全域性前置守衛

可以使用 router.beforeEach 註冊一個全域性前置守衛

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})
複製程式碼

當一個導航觸發時,全域性前置守衛會按照建立順序呼叫。守衛是非同步解析執行,此時導航在所有守衛 resolve 完之前一直處於等待中

引數

每個守衛方法接收三個引數:

  • to:Route :即將要進入的目標 路由物件
  • from:Route :當前導航正要離開的路由
  • next:Function :一定要呼叫該方法來 resolve 這個鉤子。執行效果依賴 next 方法的呼叫引數
    • next() 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是 confirmed (確認的)。
    • next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是使用者手動或者瀏覽器後退按鈕),那麼 URL 地址會重置到 from 路由對應的地址。
    • next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然後進行一個新的導航。你可以向 next 傳遞任意位置物件,且允許設定諸如 replace: truename: 'home' 之類的選項以及任何用在 router-linkto proprouter.push 中的選項。
    • next(error): (2.4.0+) 如果傳入 next 的引數是一個 Error 例項,則導航會被終止且該錯誤會被傳遞給 router.onError() 註冊過的回撥。

確保要呼叫next方法,否則鉤子就不會被resolved

全域性解析守衛

可以用router.beforeResolve註冊一個全域性守衛。這和 router.beforeEach 類似,區別是在導航被確認之前,同時在所有元件內守衛和非同步路由元件被解析之後,解析守衛就被呼叫。

全域性後置鉤子

可以註冊一個全域性後置鉤子,然而和守衛不同的是,這些鉤子不會接受next 函式也不會改變導航本身

router.afterEach((to, from) => {
  // ...
})
複製程式碼

路由獨享的守衛

可以在路由配置上直接定義 beforeEnter 守衛:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
複製程式碼

這些守衛與全域性前置守衛的方法引數是一樣的。

元件內的守衛

當然,也可以在路由元件內直接定義以下路由導航守衛:

  • beforeRouteEnter
  • beforeRouteUpdate
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染該元件的對應路由被 confirm 前呼叫
	// 也就是進入新的元件時
    // 不能獲取元件例項 `this`
    // 因為當守衛執行前,元件例項還沒被建立
  },
  beforeRouteUpdate (to, from, next) {
    // 在當前路由改變,但是該元件被複用時呼叫
    // 舉例來說,對於一個帶有動態引數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
    // 由於會渲染同樣的 Foo 元件,因此元件例項會被複用。而這個鉤子就會在這個情況下被呼叫。
    // 可以訪問元件例項 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 導航離開該元件的對應路由時呼叫
    // 可以訪問元件例項 `this`
  }
}
複製程式碼

beforeRouteEnter 守衛 不能 訪問 this,因為守衛在導航確認前被呼叫,因此即將登場的新元件還沒被建立。

不過,你可以通過傳一個回撥給 next 來訪問元件例項。在導航被確認的時候執行回撥,並且把元件例項作為回撥方法的引數。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通過 `vm` 訪問元件例項
  })
}
複製程式碼

注意 beforeRouteEnter 是支援給 next 傳遞迴調的唯一守衛。對於 beforeRouteUpdatebeforeRouteLeave 來說,this 已經可用了,所以不支援傳遞迴調,因為沒有必要了。

beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}
複製程式碼

beforeRouteUpdate 是在路徑發生改變時呼叫

這個離開守衛通常用來禁止使用者在還未儲存修改前突然離開。該導航可以通過 next(false) 來取消。

beforeRouteLeave (to, from , next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}
複製程式碼

完整的導航解析流程

1.導航被觸發
2.在失活的元件裡呼叫離開守衛。
3.呼叫全域性的 beforeEach 守衛。
4.在重用的元件裡呼叫 beforeRouteUpdate 守衛 (2.2+)。
5.在路由配置裡呼叫 beforeEnter
6.解析非同步路由元件。
7.在被啟用的元件裡呼叫 beforeRouteEnter
8.呼叫全域性的 beforeResolve 守衛 (2.5+)。
9.導航被確認。
10.呼叫全域性的 afterEach 鉤子。
11.觸發 DOM 更新。
12.用建立好的例項呼叫 beforeRouteEnter 守衛中傳給 next 的回撥函式。

程式碼示例

//Router定義

Vue.use(Router)

const router = new Router({
    ...
})

//導航守衛

router.beforeEach((to, from, next) => {
  console.log("導航前置守衛: beforeEach,");
  next();
})
router.afterEach((to, from) => {
  console.log("導航後置守衛: afterEach,");
}) 
router.beforeResolve ((to, from, next) => {
  console.log("導航解析守衛: beforeResolve,");
  next();
})
複製程式碼

元件鉤子:

export default {
  //鉤子
  beforeCreate(){
    console.log("元件鉤子: beforeCreate");
  },
  created(){
    console.log("元件鉤子: created");
  },
  beforeMount(){
    console.log("元件鉤子: beforeMount");
  },
  mounted(){
    console.log("元件鉤子: mounted");
  },
  beforeUpdate(){
    console.log("元件鉤子: beforeUpdate");
  },
  updated(){
    console.log("元件鉤子: updated");
  },
  beforeDestroy(){
    console.log("元件鉤子: beforeDestroy");
  },
  destoryed(){
    console.log("元件鉤子: destoryed");
  },
  beforeRouteEnter (to, from, next) {
    console.log("元件內部守衛: beforeRouteEnter,");
    next()
  },
  beforeRouteUpdate (to, from, next) {
    console.log("元件內部守衛: beforeRouteUpdate,");
    next()
  },
  beforeRouteLeave (to, from, next) {
    console.log("元件內部守衛: beforeRouteLeave,");
    next()
  }
}
複製程式碼

執行輸出順序:

    導航前置守衛: beforeEach
    元件內部守衛: beforeRouteEnter
    導航解析守衛: beforeResolve
    導航後置守衛: afterEach
    元件鉤子: beforeCreate
    元件鉤子: created
    元件鉤子: beforeMount
    元件鉤子: mounted
    //執行跳轉
    元件內部守衛: beforeRouteLeave
    導航前置守衛: beforeEach
    導航解析守衛: beforeResolve
    導航後置守衛: afterEach
    元件鉤子: beforeDestroy
複製程式碼

關於 Vue元件 的生命週期與鉤子函式,請參考我的文章《Vue 生命週期與鉤子函式》


^_<

相關文章