数据获取
有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:
导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
从技术角度讲,两种方式都不错 —— 就看你想要的用户体验是哪种。
导航完成后获取数据
当你使用这种方式时,我们会马上导航和渲染组件,然后在组件中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。
假设我们有一个 Post
组件,需要基于 route.params.id
获取文章数据:
vue
<template>
<div class="post">
<div v-if="loading" class="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>
<script setup>
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { getPost } from './api.js'
const route = useRoute()
const loading = ref(false)
const post = ref(null)
const error = ref(null)
// 侦听路由的参数,以便再次获取数据
watch(() => route.params.id, fetchData, { immediate: true })
async function fetchData(id) {
error.value = post.value = null
loading.value = true
try {
// 用获取数据的工具函数 / API 包裹器替换 `getPost`
post.value = await getPost(id)
} catch (err) {
error.value = err.toString()
} finally {
loading.value = false
}
}
</script>
vue
<template>
<div class="post">
<div v-if="loading" class="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>
<script>
import { getPost } from './api.js'
export default {
data() {
return {
loading: false,
post: null,
error: null,
}
},
created() {
// 侦听路由的参数,以便再次获取数据
this.$watch(
() => this.$route.params.id,
this.fetchData,
// 组件创建完后获取数据,
// 此时 data 已经被监听了
{ immediate: true }
)
},
methods: {
async fetchData(id) {
this.error = this.post = null
this.loading = true
try {
// 用获取数据的工具函数 / API 包裹器替换 `getPost`
this.post = await getPost(id)
} catch (err) {
this.error = err.toString()
} finally {
this.loading = false
}
},
},
}
</script>
在导航完成前获取数据
通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 beforeRouteEnter
守卫中获取数据,当数据获取成功后只调用 next
方法:
js
export default {
data() {
return {
post: null,
error: null,
}
},
beforeRouteEnter(to, from, next) {
try {
const post = await getPost(to.params.id)
// `setPost` 方法定义在下面的代码中
next(vm => vm.setPost(post))
} catch (err) {
// `setError` 方法定义在下面的代码中
next(vm => vm.setError(err))
}
},
// 路由改变前,组件就已经渲染完了
// 逻辑稍稍不同
async beforeRouteUpdate(to, from) {
this.post = null
getPost(to.params.id).then(this.setPost).catch(this.setError)
},
methods: {
setPost(post) {
this.post = post
},
setError(err) {
this.error = err.toString()
}
}
}
在为后面的视图获取数据时,用户会停留在当前的界面,因此建议在数据获取期间,显示一些进度条或者别的指示。如果数据获取失败,同样有必要展示一些全局的错误提醒。