生命周期与数据请求

博客详情页要显示文章内容,就必须在组件"上场"时去请求接口。Vue 组件从创建、挂载到销毁会经历一系列阶段,每个阶段都提供钩子让我们插入代码,统称生命周期钩子。本节以"进入详情页拉取文章数据"为例,讲清常用钩子与请求时机。

组件的一生:常用钩子一览

组合式 API 用 onXxx 函数注册钩子,最常用的有:

  • onBeforeMount:组件渲染到页面前触发,此时 DOM 还没生成。
  • onMounted:组件挂载完成、DOM 已就绪,请求数据的最佳时机
  • onBeforeUnmount:组件即将销毁,可清理定时器、取消请求。
  • onUnmounted:组件已销毁。

在 onMounted 中请求数据

以 axios 请求文章详情为例:

<!-- PostDetail.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import axios from 'axios'
const route = useRoute()
const post = ref(null)     // 文章数据,初始为空
const loading = ref(true)  // 是否正在加载
onMounted(async () => {
  // 组件挂载后立刻请求,:id 来自路由参数
  const { data } = await axios.get(`/api/posts/${route.params.id}`)
  post.value = data
  loading.value = false
})
</script>
<template>
  <p v-if="loading">加载中...</p>
  <article v-else>
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
  </article>
</template>

运行后页面先显示"加载中...",接口返回后渲染出标题与正文。

用 loading / error 管理请求状态

真实请求可能失败,用三个响应式状态区分界面三种情况:

const post = ref(null)
const loading = ref(true)
const error = ref('') // 错误信息
async function loadPost() {
  try {
    const { data } = await axios.get('/api/posts/1')
    post.value = data
  } catch (e) {
    error.value = '文章加载失败,请稍后重试' // 出错提示
  } finally {
    loading.value = false // 无论成败都结束加载态
  }
}

模板里用 v-if="loading"v-else-if="error"v-else 三选一渲染,页面永远处于明确状态,不会一直转圈或白屏。

路由参数变化时重新请求

若详情页被复用而参数变化,组件不会重新挂载,onMounted 不会再次触发。监听路由参数、变化时重新请求:

import { watch } from 'vue'
// 每次 id 变化都重新拉取数据
watch(() => route.params.id, async (id) => {
  const { data } = await axios.get(`/api/posts/${id}`)
  post.value = data
}, { immediate: true }) // immediate 让首次进入也立即执行

离开页面时清理:onBeforeUnmount

定时刷新、监听滚动等副作用要记得销毁,否则会内存泄漏:

let timer
onMounted(() => {
  timer = setInterval(refreshViewCount, 5000) // 每 5 秒刷新阅读量
})
onBeforeUnmount(() => {
  clearInterval(timer) // 组件销毁前清除定时器
})

小结:onMounted 适合发起数据请求,配合 loading/error 让界面清晰;路由参数变化用 watch 重新请求,离开页面用 onBeforeUnmount 清理资源。下一节用 watch 实现博客的实时搜索。

笔记加载中…