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

Nuxt3:如何在页面中间件复用已获取数据避免重复请求?

Nuxt3 页面中间件复用请求数据避免重复请求的解决方案

问题场景

我在开发Nuxt3项目时,为提升SEO友好性,采用pages/posts/[id]/[title].vue路由替代原有的pages/posts/[id].vue。但在页面setup中通过useFetch获取数据后,尝试在definePageMeta的中间件里访问该数据时,出现500错误提示“data is not defined”。我不想在中间件里重复发起请求,请问如何实现仅一次请求且能在中间件中访问数据?

原代码如下:

type Post {
    title: string;
};

const route = useRoute();

const { data } = await useFetch(
    `/posts/${route.params.id}`
) as {
    data: Ref<Post>
};

definePageMeta({
    middleware: [
        (to: RouteLocationNormalized, from: RouteLocationNormalized) => {
            // 想在此处访问已获取的数据
            if (to.params.title !== data.value.title) {
                navigateTo(`/posts/${data.value.id}/${data.value.title}`);
            }
        }
    ]
});

报错原因

核心问题是Nuxt3的路由中间件运行时机早于页面组件的setup函数:当中间件执行时,setup里声明的data变量还未完成初始化,自然无法被访问。


解决方案

方案一:用useAsyncData缓存实现数据复用

通过封装可复用的composable,结合useAsyncData的缓存特性,让中间件和页面共享同一份请求数据,彻底避免重复请求。

  1. 创建封装请求的composable
// composables/usePost.ts
type Post = {
  id: string;
  title: string;
};

export const usePost = (id: string) => {
  return useAsyncData(`post-${id}`, () => $fetch(`/posts/${id}`), {
    cache: true, // 启用缓存,同ID的请求只会执行一次
    transform: (res) => res as Post
  });
};
  1. 创建独立的路由中间件
// middleware/validatePostTitle.ts
export default defineNuxtRouteMiddleware(async (to) => {
  const id = to.params.id as string;
  const { data: post } = await usePost(id);
  
  if (!post.value) {
    // 处理数据获取失败的情况,比如跳转到404页面
    return navigateTo('/404');
  }
  
  if (to.params.title !== post.value.title) {
    return navigateTo(`/posts/${post.value.id}/${post.value.title}`);
  }
});
  1. 在页面中使用
// pages/posts/[id]/[title].vue
const route = useRoute();
// 直接复用中间件已请求的缓存数据
const { data: post } = await usePost(route.params.id as string);

definePageMeta({
  middleware: ['validatePostTitle']
});

方案二:用页面validate函数替代中间件

Nuxt3的页面validate函数支持异步操作,且可以直接在页面上下文内共享数据,无需额外创建中间件文件。

// pages/posts/[id]/[title].vue
type Post = {
  id: string;
  title: string;
};

// 用useState存储请求结果,实现页面内数据共享
const postState = useState<Post | null>('post', () => null);

definePageMeta({
  validate: async (to) => {
    const id = to.params.id as string;
    const post = await $fetch(`/posts/${id}`) as Post;
    postState.value = post;
    
    if (to.params.title !== post.title) {
      // 返回跳转路径,Nuxt会自动执行导航
      return `/posts/${post.id}/${post.title}`;
    }
    // 返回true表示验证通过,允许进入页面
    return true;
  }
});

// 页面内直接使用已获取的数据
const post = postState;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:32:50