数据获取
前言
在 Next.js 中如何获取数据呢?
Next.js 优先推荐使用原生的 fetch 方法,因为 Next.js 拓展了原生的 fetch 方法,为其添加了缓存和更新缓存(重新验证)的机制。
这样做的好处在于可以自动复用请求数据,提高性能。坏处在于如果你不熟悉,经常会有一些“莫名奇妙”的状况出现……
让我们来看看具体如何使用吧。
服务端使用 fetch
基本用法
Next.js 拓展了原生的 fetch Web API
,可以为服务端的每个请求配置缓存(caching)和重新验证( revalidating)行为。
你可以在服务端组件、路由处理程序、Server Actions 中搭配 async/await
语法使用 fetch
。
举个例子:
// app/page.js
async function getData() {
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
if (!res.ok) {
// 由最近的 error.js 处理
throw new Error("Failed to fetch data");
}
return res.json();
}
export default async function Page() {
const data = await getData();
return <main>{JSON.stringify(data)}</main>;
}
默认缓存(15 已经去掉了)
默认情况下,Next.js 会缓存每个请求
// fetch 的 cache 选项用于控制该请求的缓存行为
// 默认就是 'force-cache', 平时写的时候可以省略
fetch("https://...", { cache: "force-cache" });
- 但这些情况不会被自动缓存
注意
在 Server Action 中使用的时候
在定义了非 GET 方法的路由处理程序中使用的时候
重新验证
在 Next.js
中,清除数据缓存并重新获取最新数据的过程就叫做重新验证(Revalidation
)。
Next.js 提供了两种方式重新验证:
一种是基于时间的重新验证(Time-based revalidation),即经过一定时间并有新请求产生后重新验证数据,适用于不经常更改且新鲜度不那么重要的数据。
一种是按需重新验证(On-demand revalidation),根据事件手动重新验证数据。按需重新验证又可以使用基于标签(tag-based)和基于路径(path-based)两种方法重新验证数据。适用于需要尽快展示最新数据的场景。
基于时间的重新验证
使用基于时间的重新验证,你需要在使用 fetch 的时候设置 next.revalidate
选项(以秒为单位):
fetch("https://...", { next: { revalidate: 3600 } });
- 或者通过路由段配置项进行验证,使用这种方法,它会验证该路由段的所有 fetch 请求
// layout.jsx | page.jsx | route.js
export const revalidate = 0; // 0 表示不使用缓存
- 注意
在一个静态渲染的路由中,如果你有多个请求,每个请求设置了不同的重新验证时间,将会使用最短的时间用于所有的请求。而对于动态渲染的路由,每一个 fetch 请求都将独立重新验证。
按需重新验证(复杂)
- revalidatePath
import { revalidatePath } from "next/cache";
export async function GET(request) {
const path = request.nextUrl.searchParams.get("path");
if (path) {
revalidatePath(path);
return Response.json({ revalidated: true, now: Date.now() });
}
return Response.json({
revalidated: false,
now: Date.now(),
message: "Missing path to revalidate",
});
}
- revalidateTag(了解就好)
Next.js 有一个路由标签系统,可以跨路由实现多个 fetch 请求重新验证。具体这个过程为:
使用 fetch 的时候,设置一个或者多个标签标记请求
调用 revalidateTag 方法重新验证该标签对应的所有请求
举个例子:
// app/page.js
export default async function Page() {
const res = await fetch("https://...", { next: { tags: ["collection"] } });
const data = await res.json();
// ...
}
在这个例子中,为 fetch
请求添加了一个 collection
标签。在 Server Action
中调用 revalidateTag
,就可以让所有带 collection
标签的 fetch
请求重新验证。
// app/actions.js
"use server";
import { revalidateTag } from "next/cache";
export default async function action() {
revalidateTag("collection");
}
让我们真的写个例子。修改 app/page.js 代码如下:
async function getData() {
const res = await fetch("https://api.thecatapi.com/v1/images/search", {
next: { tags: ["collection"] },
});
if (!res.ok) {
throw new Error("Failed to fetch data");
}
return res.json();
}
export default async function Page() {
const data = await getData();
return <img src={data[0].url} width="300" />;
}
退出数据缓存
注意
当fetch
满足这些条件的时候 会退出数据缓存:
fetch 请求添加了 cache: 'no-store' 选项
fetch 请求添加了 revalidate: 0 选项
fetch 请求在路由处理程序中并使用了 POST 方法
使用 headers 或 cookies 的方法之后使用 fetch 请求
配置了路由段选项 const dynamic = 'force-dynamic'
配置了路由段选项 fetchCache ,默认会跳过缓存
-fetch 请求使用了 Authorization 或者 Cookie 请求头,并且在组件树中其上方还有一个未缓存的请求
在具体使用的时候,如果你不想缓存某个单独请求:
// layout.js | page.js
fetch("https://...", { cache: "no-store" });
针对多个请求,页面里面可以使用
// layout.js | page.js
export const dynamic = "force-dynamic";
如果使用第三方库的话
页面里面设置 export const dynamic = 'force-dynamic'