# 組件守衛
組件守衛,就是如果當前路由匹配成功,那么對應的組件就會顯示到頁面中。同樣的在組件顯示的過程中,也會經歷三個組件守衛。那么這些守衛,可以用來做些什么呢?
`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`
}
}
```
# 用途數據獲取
有時候,進入某個路由后,需要從服務器獲取數據。例如,在渲染用戶信息時,你需要從服務器獲取用戶的數據。我們可以通過兩種方式來實現:
* **導航完成之后獲取**:先完成導航,然后在接下來的組件生命周期鉤子中獲取數據。在數據獲取期間顯示“加載中”之類的指示。
* **導航完成之前獲取**:導航完成前,在路由進入的守衛中獲取數據,在數據獲取成功后執行導航。
從技術角度講,兩種方式都不錯 —— 就看你想要的用戶體驗是哪種。
## 導航完成后獲取數據
當你使用這種方式時,我們會馬上導航和渲染組件,然后在組件的`created`鉤子中獲取數據。這讓我們有機會在數據獲取期間展示一個 loading 狀態,還可以在不同視圖間展示不同的 loading 狀態。
假設我們有一個`Post`組件,需要基于`$route.params.id`獲取文章數據:
```
<template>
<div class="post">
<div class="loading" v-if="loading">
Loading...
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="post" class="content">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
</div>
</template>
```
```
export default {
data () {
return {
loading: false,
post: null,
error: null
}
},
created () {
// 組件創建完后獲取數據,
// 此時 data 已經被 observed 了
this.fetchData()
},
watch: {
// 如果路由有變化,會再次執行該方法
'$route': 'fetchData'
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// replace getPost with your data fetching util / API wrapper
getPost(this.$route.params.id, (err, post) => {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.post = post
}
})
}
}
}
```
## 在導航完成前獲取數據
通過這種方式,我們在導航轉入新的路由前獲取數據。我們可以在接下來的組件的`beforeRouteEnter`守衛中獲取數據,當數據獲取成功后只調用`next`方法。
```
export default {
data () {
return {
post: null,
error: null
}
},
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
// 路由改變前,組件就已經渲染完了
// 邏輯稍稍不同
beforeRouteUpdate (to, from, next) {
this.post = null
getPost(to.params.id, (err, post) => {
this.setData(err, post)
next()
})
},
methods: {
setData (err, post) {
if (err) {
this.error = err.toString()
} else {
this.post = post
}
}
}
}
```
在為后面的視圖獲取數據時,用戶會停留在當前的界面,因此建議在數據獲取期間,顯示一些進度條或者別的指示。如果數據獲取失敗,同樣有必要展示一些全局的錯誤提醒。