Next.js全局API复用致页面卡顿,求正确实现方案
问题分析与解决方案
问题背景
在Next.js博客应用中,多个页面(文章、分类、标签、作者等)需要重复调用两个相同的外部API获取内容。为避免重复代码,我尝试创建全局API调用,代码如下:
import { getAuthors } from "@/libs/getAuthors"; import { getPosts } from "@/libs/getPosts"; export async function getStaticProps() { return { props: { posts: getPosts(), authors: getAuthors(), }, revalidate: 1, }; }
同时在next.config.js中添加了以下配置:
module.exports = { images: { domains: ["images.ctfassets.net"], }, webpack: (config) => { config.experiments = { topLevelAwait: true, layers: true }; return config; }, };
虽然方案能运行,但页面加载速度大幅变慢。请问原因是什么?创建并使用此类全局API调用的正确方式是什么?
补充的getAuthors代码:
import { createClient } from "contentful"; const client = createClient({ space: process.env.CONTENTFUL_SPACE_ID, accessToken: process.env.CONTENTFUL_ACCESS_KEY, }); const auth_data = await client.getEntries({ content_type: "author" }); export function getAuthors() { var authors = []; var auth_len = auth_data.items.length; for (var i = 0; i < auth_len; i++) { authors.push({ authorSlug: auth_data.items[i].fields.name .toString() .replace(/ /g, "-") .toLowerCase(), authorContent: auth_data.items[i].fields.description, authorFrontMatter: { title: auth_data.items[i].fields.name, image: "https:" + auth_data.items[i].fields.image.fields.file.url, }, }); } return authors; }
页面加载变慢的原因
核心问题出在顶层await的错误使用:
- 你在
getAuthors.js模块的顶层直接执行await client.getEntries(...),这会导致模块加载时就触发API请求,且请求会阻塞整个模块的加载流程——所有依赖该模块的页面,都要等API请求完成后才能继续渲染,直接拖慢页面启动速度。 - 若多个页面都导入
getAuthors,会重复触发相同的API请求(模块缓存对顶层await的处理不可靠),进一步增加服务器和网络开销。 - 另外,
getStaticProps中调用getPosts()和getAuthors()时未使用await,但实际依赖的是顶层await获取的数据,无法并行处理两个API请求,进一步降低效率。
正确的全局API调用方式
要解决重复代码和性能问题,需将API请求逻辑封装为异步函数,在页面的getStaticProps/getServerSideProps中按需调用,同时利用缓存机制避免重复请求。
步骤1:重构API封装函数
把getAuthors和getPosts改成异步函数,不在模块顶层执行请求:
// @/libs/getAuthors.js import { createClient } from "contentful"; const client = createClient({ space: process.env.CONTENTFUL_SPACE_ID, accessToken: process.env.CONTENTFUL_ACCESS_KEY, }); export async function getAuthors() { // 仅在函数调用时发起API请求 const auth_data = await client.getEntries({ content_type: "author" }); return auth_data.items.map(item => ({ authorSlug: item.fields.name .toString() .replace(/ /g, "-") .toLowerCase(), authorContent: item.fields.description, authorFrontMatter: { title: item.fields.name, image: "https:" + item.fields.image.fields.file.url, }, })); }
同理重构getPosts函数,确保请求在函数内部异步执行。
步骤2:在页面中并行调用API
在需要数据的页面getStaticProps中,用Promise.all并行执行两个API请求,减少总耗时:
import { getAuthors } from "@/libs/getAuthors"; import { getPosts } from "@/libs/getPosts"; export async function getStaticProps() { // 并行请求,提升效率 const [posts, authors] = await Promise.all([ getPosts(), getAuthors() ]); return { props: { posts, authors }, revalidate: 1, // ISR缓存,1秒后重新验证内容 }; }
步骤3:移除不必要的Webpack配置
因为不再使用顶层await,可删除next.config.js中的topLevelAwait配置:
module.exports = { images: { domains: ["images.ctfassets.net"], }, // 移除以下配置 // webpack: (config) => { // config.experiments = { topLevelAwait: true, layers: true }; // return config; // }, };
可选:添加请求缓存
若希望在多个页面间共享API请求结果,可实现简单的内存缓存:
// @/libs/getAuthors.js import { createClient } from "contentful"; const client = createClient({ space: process.env.CONTENTFUL_SPACE_ID, accessToken: process.env.CONTENTFUL_ACCESS_KEY, }); // 内存缓存,避免重复请求 let cachedAuthors = null; const CACHE_EXPIRE = 5 * 60 * 1000; // 5分钟过期 export async function getAuthors() { if (cachedAuthors) { return cachedAuthors; } const auth_data = await client.getEntries({ content_type: "author" }); cachedAuthors = auth_data.items.map(item => ({ authorSlug: item.fields.name .toString() .replace(/ /g, "-") .toLowerCase(), authorContent: item.fields.description, authorFrontMatter: { title: item.fields.name, image: "https:" + item.fields.image.fields.file.url, }, })); // 设置缓存过期 setTimeout(() => { cachedAuthors = null; }, CACHE_EXPIRE); return cachedAuthors; }
这样多个页面调用getAuthors时,只会发起一次API请求,后续直接返回缓存数据,进一步提升性能。
内容的提问来源于stack exchange,提问作者Fjott
相关产品推荐
相关产品推荐

