NextJS数据获取
About 1685 wordsAbout 6 min
NextJSReactFullstackSSR
2025-07-07
Page Router模式
getStaticProps
当你在页面组件里定义了getStaticProps
函数,意味着这个页面在部署前就已经生成好了静态HTML页面。
函数只在代码构建阶段运行,不是请求时运行
。因此不接受请求参数。开发模式下,函数会在每次request请求时都执行
函数里不要编写客户端执行代码,因为它是在服务端执行的。
不要在函数里请求API Route,即api文件夹里的路由。(都在服务端执行,没必要通过网络请求去调用同服务段的其他代码逻辑;api route供客户端、其他外部服务调用;getStaticProps函数执行时api route不确保可用)
nextjs构建阶段执行该函数生成的结果会被存储在相应的json文件里,客户端导航到当前页面时会,读取json文件里的数据作为props传给页面组件。
getStaticProps
只能在页面组件里使用静态增量再生ISR
: 按需重生机制,不是定时任务机制。即在revalidate`字段返回的秒数后,如果有新的请求,会重新生成页面。
type Props = {
params: any, // 路由参数,如页面名称是[id].js,params.id
preview?: boolean, // 是否是预览模式,预览模式下,会返回previewData
previewData?: any, // 预览模式下,返回的数据,
locale?: string, // 当前语言
locales?: string[], // 所有语言
defaultLocale?: string, // 默认语言
}
export async function getStaticProps(context:Props) {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
revalidate: 10, //单位秒,可选
notFound: false, //可选,默认为false,当触发指定逻辑时,可将值设为true,将返回404,提示页面不存在
redirect: { //可选,默认为undefined,当触发指定逻辑时,可将值设为对象,页面重定向
destination: '/',
permanent: false, //可选,默认为false,告诉搜索引擎是临时重定向还是永久重定向
},
statusCode: 200, //可选,默认为200,当触发指定逻辑时,可将值设为其他状态码,如404、500等
}
}
作用和getStaticProps
类似,但generateStaticParams
可以生成多个页面。
getStaticPaths
当页面名称里带路由参数时,必须使用getStaticPaths
来预先生成页面。
- 函数只在代码构建阶段运行,不是请求时运行。因此不接受请求参数。
开发模式下,函数会在每次request请求时都执行
- 动态路由页面,必须导出一个·
getStaticPaths
函数。函数返回值必须包括
type StaticPathsResult = {
// 页面路径列表,构建时预先生成页面列表
paths: Array<{
params: {
[paramName: string]: string; //paramName是路由参数名,如页面名称是[id].js,paramName为id
}
}>,
fallback: boolean | 'blocking' //可选,默认为false。定义访问页面没有在预先生成的页面列表里时,如何处理
// 当fallback为true时,没有匹配到页面时,Server端重新获取数据生成页面; 客户端根据useRoute().isFallback判断后端是否正在生成页面,并显示一个loading状态。页面生成后,客户端会自动刷新显示页面内容,页面被缓存。
// 当fallback为blocking时,没有匹配到页面时,客户端会一直显示阻塞,直到页面生成,页面被缓存。
// 当fallback为false时,页面会显示404页面。
}
getServerSideProps
如果页面导出了getServerSideProps函数,则该页面会在每次请求时都会在服务端执行这个函数。
type Props = {
params: any, // 路由参数,如页面名称是[id].js,params.id
req: any, // 请求对象
res: any, // 响应对象
query: any, // 查询参数
preview?: boolean, // 是否是预览模式,预览模式下,会返回previewData
previewData?: any, // 预览模式下,返回的数据,
resolvedUrl?: string, // 解析后的URL,包含查询参数
locale?: string, // 当前语言
locales?: string[], // 所有语言
defaultLocale?: string, // 默认语言
}
export async function getServerSideProps(context:Props) {
return {
props: {}, // will be passed to the page component as props
}
}
getServerSideProps
函数的返回值类型,和getStaticProps
函数的返回值类型相同。
SWR 或 React Query
当不需要服务端渲染时,可以使用SWR或React Query。
核心思想是先返回缓存中的旧数据(stale),同时在后台发起新的请求来验证(revalidate)数据,一旦新数据返回,就更新 UI
。
React Query相比SWR,功能更全面,体积更大。
React Query
import { useQuery, QueryClient, QueryClientProvider } from '@tanstack/react-query';
// 创建一个 QueryClient 实例
const queryClient = new QueryClient();
// 数据获取器函数
const fetchUser = async () => {
const res = await fetch('/api/user');
if (!res.ok) {
throw new Error('Network response was not ok');
}
return res.json();
};
function Profile() {
// useQuery 的第一个参数是唯一的 queryKey,第二个是查询函数
const { data, error, isLoading } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
if (isLoading) return <div>Loading profile...</div>;
if (error) return <div>Failed to load user data: {error.message}</div>;
return (
<div>
<h1>Welcome, {data.name}!</h1>
<p>Email: {data.email}</p>
</div>
);
}
function App() {
return (
// 使用 QueryClientProvider 包裹应用,使其能够访问 queryClient
<QueryClientProvider client={queryClient}>
<Profile />
</QueryClientProvider>
);
}
export default App;
预览模式
Page Router和App Route都支持预览模式。使用区别在于App Route模式下,预览模式的判断不同
import { draftMode } from 'next/headers';
import { redirect, notFound} from 'next/navigation';
//...
const { isEnabled } = draftMode(); // 检查当前请求是否处于预览模式
//...
配合getStaticProps使用
。这是因为 getStaticProps 默认只在构建时运行一次,并且只获取“已发布”的数据。这意味着一旦页面生成,它就不会自动更新以反映 CMS 中的草稿内容。预览模式正是为了解决这个痛点而生的,它提供了一个机制来:
- 在用户进入预览模式时,跳过静态缓存。
- 在 getStaticProps 内部,通过 context.preview 标识,条件性地从 CMS 请求最新的、可能未发布的草稿数据。
- 确保在浏览器端显示这些最新数据,同时又不影响生产环境的静态优化。
// 步骤1:
// 1. 创建一个名为preview.js的api route。
// 2. 在preview.js里,使用setPreviewData方法激活预览模式。
// 3. 重定向到要预览的页面路径。
// pages/api/preview.js
export default function handler(req, res) {
// 1. 验证你的秘密令牌 (可选但强烈推荐)
if (req.query.secret !== process.env.MY_PREVIEW_SECRET_TOKEN || !req.query.slug) {
return res.status(401).json({ message: 'Invalid token or missing slug' });
}
// 2. 根据你的 CMS API 获取草稿数据
const data:any;
//...
// 3. 激活预览模式并重定向到目标页面
res.setPreviewData(data);
// 重定向到要预览的页面路径
res.redirect(`/posts/${req.query.slug}`);
res.end();
}
// 步骤2:
// 在页面组件里,使用使用previewData作为props。
// 步骤3.
// 指定交互调用preview.js的路由。
// 如:
// <Link href={`/api/preview?slug=${post.slug}&secret=${process.env.MY_PREVIEW_SECRET_TOKEN}`}>