You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt 3 SSR页面即时渲染并异步填充API内容的最优方案

Nuxt 3 SSR 即时渲染布局与导航、延迟展示内容的实现方案

一、实现思路与步骤

要实现布局/导航栏即时渲染,同时内容区等待API请求完成后展示(pending时显示骨架屏),核心是拆分布局与页面内容的渲染逻辑:

  1. 分离全局布局与页面内容
    将导航栏、页头等通用框架放在Nuxt的layouts/default.vue中,这部分属于与页面数据无关的全局结构,会在SSR阶段直接渲染输出,用户打开页面就能立刻看到完整的布局框架。

  2. 页面内容区处理加载状态
    在具体页面组件(比如pages/index.vue)中,通过useFetch或useAsyncData请求数据,利用返回的pending状态控制骨架屏和真实内容的切换:

    • 当pending为true时,显示骨架屏占位
    • 当pending为false时,替换为真实API数据内容

代码示例

布局文件 layouts/default.vue

<template>
  <div class="app-container">
    <!-- 导航栏:SSR直接渲染,无需等待API -->
    <header class="navbar">
      <div class="logo">我的博客</div>
      <nav>
        <a href="/">首页</a>
        <a href="/archive">归档</a>
        <a href="/about">关于</a>
      </nav>
    </header>
    <!-- 页面内容插槽:加载状态在这里处理 -->
    <main class="page-wrapper">
      <slot />
    </main>
  </div>
</template>

<style scoped>
.navbar {
  display: flex;
  justify-content: space-between;
  padding: 1rem 2rem;
  background: #2c3e50;
  color: white;
}
.navbar a {
  color: white;
  margin-left: 1.5rem;
  text-decoration: none;
}
.page-wrapper {
  max-width: 1200px;
  margin: 2rem auto;
  padding: 0 1rem;
}
</style>

页面文件 pages/index.vue

<template>
  <div class="home-page">
    <!-- API请求pending时显示骨架屏 -->
    <div v-if="pending" class="skeleton-list">
      <div class="skeleton-item" v-for="i in 3" :key="i">
        <div class="skeleton-title"></div>
        <div class="skeleton-text"></div>
      </div>
    </div>

    <!-- 请求完成后展示真实内容 -->
    <div v-else class="post-list">
      <article v-for="post in data" :key="post.id" class="post-card">
        <h2>{{ post.title }}</h2>
        <p>{{ post.excerpt }}</p>
        <a :href="`/posts/${post.id}`">阅读全文</a>
      </article>
    </div>
  </div>
</template>

<script setup>
// 发起API请求,可根据需求配置server选项
const { data, pending } = await useFetch('https://api.example.com/posts', {
  // 若不需要服务端预取数据(比如用户私有内容、非SEO页面),开启server: false
  // server: false
})
</script>

<style scoped>
/* 骨架屏样式 */
.skeleton-list {
  gap: 1.5rem;
  display: flex;
  flex-direction: column;
}
.skeleton-item {
  padding: 1rem;
  background: #f5f5f5;
  border-radius: 8px;
}
.skeleton-title {
  height: 24px;
  width: 60%;
  background: #e0e0e0;
  border-radius: 4px;
  margin-bottom: 0.8rem;
}
.skeleton-text {
  height: 16px;
  width: 100%;
  background: #e0e0e0;
  border-radius: 4px;
}

/* 真实内容样式 */
.post-list {
  gap: 1.5rem;
  display: flex;
  flex-direction: column;
}
.post-card {
  padding: 1.5rem;
  border: 1px solid #eee;
  border-radius: 8px;
}
.post-card h2 {
  margin-top: 0;
  color: #2c3e50;
}
</style>

二、关于useFetch({server: false})是否为通用/最佳实践

这取决于你的业务场景:

  1. 适合用server: false的场景

    • 页面内容是用户私有数据(比如个人中心、购物车),服务端无法提前预取
    • 页面不需要SEO优化(爬虫不需要读取内容)
    • 想减轻服务端负载,把请求压力转移到客户端
      这种场景下,用server: false是合理的,搭配pending状态做骨架屏也是通用做法。
  2. 不适合用server: false的场景

    • 页面需要SEO:如果开启server: false,服务端不会预取数据,返回的HTML中内容区是空的,爬虫无法抓取到真实内容,影响SEO
    • 首屏性能要求高:服务端预取数据后直接返回渲染好的内容,用户不需要等待客户端发起请求,首屏加载体验更好
      这种场景下,应该保持server: true(useFetch的默认值),服务端会提前请求数据并渲染真实内容,客户端仅在路由切换时会触发pending状态显示骨架屏。
  3. 通用结论

    • pending状态判断加载状态的方式是通用且推荐的,无论请求在服务端还是客户端发起都适用
    • server: false不是通用最佳实践,需要根据页面是否需要SEO、是否为用户私有数据来选择是否开启

内容的提问来源于stack exchange,提问作者Simon Fischer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 00:40:44