跳至内容

数据获取

有时您需要在激活路由时从服务器获取数据。例如,在渲染用户资料之前,您需要从服务器获取用户数据。我们可以通过两种不同的方式实现这一点

  • 导航后获取:先执行导航,然后在传入组件的生命周期钩子中获取数据。在获取数据时显示加载状态。

  • 导航前获取:在路由进入守卫中获取数据,并在数据获取完成后执行导航。

从技术上讲,这两种方法都是有效的选择 - 最终取决于您要实现的用户体验。

导航后获取

使用这种方法时,我们会立即导航并渲染传入组件,并在组件本身中获取数据。它使我们有机会在网络获取数据时显示加载状态,我们还可以为每个视图以不同的方式处理加载。

假设我们有一个 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 the params of the route to fetch the data again
watch(() => route.params.id, fetchData, { immediate: true })

async function fetchData(id) {
  error.value = post.value = null
  loading.value = true
  
  try {
    // replace `getPost` with your data fetching util / API wrapper
    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() {
    // watch the params of the route to fetch the data again
    this.$watch(
      () => this.$route.params.id,
      this.fetchData,
      // fetch the data when the view is created and the data is
      // already being observed
      { immediate: true }
    )
  },
  methods: {
    async fetchData(id) {
      this.error = this.post = null
      this.loading = true

      try {
        // replace `getPost` with your data fetching util / API wrapper
        this.post = await getPost(id)
      } catch (err) {
        this.error = err.toString()
      } finally {
        this.loading = false
      }
    },
  },
}
</script>

导航前获取

使用这种方法,我们在实际导航到新路由之前获取数据。我们可以在传入组件的 beforeRouteEnter 守卫中执行数据获取,并且只有在获取完成时才调用 next。传递给 next 的回调将在 组件挂载后 被调用

js
export default {
  data() {
    return {
      post: null,
      error: null,
    }
  },
  async beforeRouteEnter(to, from, next) {
    try {
      const post = await getPost(to.params.id)
      // `setPost` is a method defined below
      next(vm => vm.setPost(post))
    } catch (err) {
      // `setError` is a method defined below
      next(vm => vm.setError(err))
    }
  },
  // when route changes and this component is already rendered,
  // the logic will be slightly different.
  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()
    }
  }
}

用户将在获取传入视图的资源时停留在之前的视图上。因此,建议在获取数据时显示进度条或某种指示器。如果数据获取失败,还需要显示某种全局警告消息。