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的缓存特性,让中间件和页面共享同一份请求数据,彻底避免重复请求。
- 创建封装请求的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 }); };
- 创建独立的路由中间件
// 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}`); } });
- 在页面中使用
// 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
相关产品推荐
相关产品推荐

