Nuxt3如何实现直达页SSR取数、站内导航懒加载取数
Nuxt3 差异化取数实现方案
核心逻辑是通过运行环境判断区分两类访问场景,同时满足SEO需求和客户端导航体验:
- 硬导航(地址栏输入、页面刷新、外链跳转):请求优先到达服务端执行SSR,使用阻塞式取数保证动态元数据能直接渲染到返回的HTML中,满足搜索引擎、社交爬虫的抓取要求
- 客户端内跳转(NuxtLink跳转、编程式导航):逻辑完全在客户端执行,使用非阻塞的懒加载取数,即时响应导航同时展示加载态,避免阻塞交互
具体实现代码
直接在pages/thing.vue中编写如下逻辑:
<script setup lang="ts"> const route = useRoute() // 固定请求key,保证服务端数据能被客户端hydration阶段复用 const fetchKey = `thing-page-${(route.params.id as string) || 'default'}` // 核心判断:服务端环境用阻塞式useFetch,客户端统一用非阻塞useLazyFetch const { data, pending, error } = import.meta.server ? await useFetch('/api/thing', { key: fetchKey }) : useLazyFetch('/api/thing', { key: fetchKey }) // 响应式生成页面元数据 useSeoMeta({ title: () => data.value?.title ?? '内容加载中', description: () => data.value?.description ?? '', ogTitle: () => data.value?.title, ogDescription: () => data.value?.description, ogImage: () => data.value?.coverUrl }) </script> <template> <!-- 客户端导航时的加载态 --> <div v-if="pending" class="loading-container"> 内容加载中... </div> <!-- 错误态 --> <div v-else-if="error" class="error-container"> 内容加载失败,请刷新重试 </div> <!-- 正常内容渲染 --> <div v-else class="content-container"> <h1>{{ data.title }}</h1> <p>{{ data.description }}</p> <!-- 其余页面内容 --> </div> </template>
逻辑说明
- 硬导航场景适配:当用户通过地址栏输入、刷新、外链进入页面时,服务端会优先执行页面逻辑,此时
import.meta.server值为true,会阻塞等待/api/thing接口返回后,再生成包含完整动态元数据、页面内容的HTML返回给客户端,完全满足SEO和社交卡片展示需求。客户端hydration阶段会通过相同的fetchKey直接复用服务端注入的预取数据,不会重复发起请求,也不会出现加载态闪屏。 - 客户端导航场景适配:当用户在应用内部通过链接或编程式导航跳转到该页面时,逻辑完全在客户端执行,此时
import.meta.server值为false,会调用非阻塞的useLazyFetch发起请求,页面会立即响应跳转并展示加载态,等接口返回后再渲染实际内容,不会出现导航阻塞的问题。
注意事项
- 必须保证服务端和客户端取数的key完全一致,如果是动态路由页面(如
/thing/[id]),需要将路由参数拼入key中,避免不同页面内容的缓存互相覆盖,同时保证hydration阶段能正确复用服务端数据。 - 元数据配置请使用getter函数形式传入,保证SSR阶段能读取到预取数据,客户端异步拿到数据后也能自动更新页面meta信息。
- 如果接口依赖用户登录态,需要在fetch配置中添加
credentials: 'include',同时SSR阶段需要转发客户端请求头到接口,避免出现权限不一致的问题。
内容的提问来源于stack exchange,提问作者akamichael
相关产品推荐
相关产品推荐

