Next.js
getStaticProps
技术细节:
- 构建时运行(only runs at build time)
因为getStaticProps
在构建时运行,所以它不能接收只能在请求时可用的数据,例如查询参数或者 HTTP 请求头,来生成静态页面。 - 直接写服务端运行代码(write server-side code directly)
注意getStaticProps
只能运行在服务端,它永远不会运行在客户端。它也不会被浏览器当作JS模块使用。这意味着你可以写类似于查询数据库之类的代码,而不用担心它们被发送到浏览器。你不应该发起一个API路由请求在getStaticProps
里面,取而代之的是,你可以直接在getStaticProps
中写服务端代码。 - 静态生成HTML和JSON(statically generates both HTML and JSON)
当一个带有getStaticProps
静态方法的页面在构建时预渲染的时候,除了页面的 HTML 文件,Next.js同时生成了一个保存执行getStaticProps
后的结果的JSON文件。
这个 JSON 文件将被在客户端通过路由导航渲染的时候使用。当你导航至一个使用getStaticProps
预渲染的页面的时候,Next.js 读取这个JSON文件(在构建时已完成计算: pre-computed at build time)然后使用它作为页面组件的props。这意味着客户端渲染过渡(transitions)将不会调用getStaticProps
方法,而只有导出的 JSON 数据被使用了。
当使用增量式静态生成(Incremental Static Generation)时getStaticProps
将被执行out of band,以生成被客户端导航所需的JSON。你可能会看到相同的页面有多次请求,然而这是可预期的(intended)并且对终端用户体验没有影响的。 - 只在页面中被允许(only allowed in a page)
getStaticProps
只能从一个页面中导出。你不能从一个非页面的文件导出它。这个限定的其中一个原因是 React 在页面渲染之前需要有全部的数据。同样地,你必须使用export async function getStaticProps() {}
-如果你添加getStaticProps
作为一个页面组件得属性,是不会起作用的。 - 在开发环境每次请求都会执行
在开发环境(next dev
),getStaticProps
将会在每次请求的时候被执行。 - 预览模式
在一些示例中,你可能暂时想要避开静态生成,在请求时渲染页面而不是构建时。例如,你可能在使用一个无头的内容管理系统(headless CMS),然后想在发布它之前预览草稿。
这个使用场景是被 Next.js 的一个特点叫做 预览模式 得以实现的。
getStaticPaths
当一个页面有动态路由的时候,使用getStaticProps
它需要定义一个路径列表用来在构建时加载到HTML。
如果你在一个使用动态路由的页面导出一个getStaticPaths
的
异步(async
)方法,Next.js 将静态地预渲染所有由getStaticPaths
指定的路径。
export async function getStaticPaths() {
return {
paths: [
{ params: { ... } }
],
fallback: true, //false or 'blocking'
}
}
-
paths
属性是必须的
paths
属性定义了哪些路径会被预渲染。例如,如果你有一个使用动态路由的页面pages/posts/[id].js
,如果你在这个页面中导出getStaticPaths
方法并且返回如下的paths
:
return {
paths: [
{ params: { id: '1' } },
{ params: { id: '2' } }
],
fallback: ...
}
然后 Next.js 会在构建时使用这个页面组件静态生成 posts/1
和 posts/2
。
注意 params
的值必须与页面中使用的参数对应:
- 如果页面名称是
pages/posts/[postId]/[commentId]
,那么params
应该包含postId
和commentId
。 - 如果页面名称是
pages/[...slug]
,那么params
应该包含slug
,并且是一个数组。例如,如果这个数组是['foo', 'bar']
,那么 Next.js 将会静态生成页面/foo/bar
。 - 如果页面使用可选的路由,支持
null
、[]
、undefined
、false
渲染根路由。例如,如果你配置slug: false
给pages/[[...slug]]
,Next.js 将静态生成页面/
。
-
fallback
属性是必须的
fallback: false
如果fallback
是false
,那么任何路径都不会生成并且变成一个404页面。你可以在你仅有少量的路径去预渲染的时候这样做,这样的话在构建时都是静态页面。当并不经常添加新的页面的时候这样做很有用。但是当你需要然后新的时候的时候,你就需要重新构建。
这里有一个预渲染一个博客详情页面pages/posts/[id].js
的示例,博客列表会从一个内容管理系统(CMS)中获取并且通过getStaticPaths
返回。然后,每个详情页面,它通过getStaticProps
从一个内容管理系统(CMS)获取数据。这里示例如下:
// pages/posts/[id].js
function Post({ post }) {
//Render post...
}
// This function gets called at build time
export async function getStaticPaths() {
//Call an external API endpoint to get posts
const res = await fetch('https://.../posts');
const posts = await res.json()
//Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
//We`ll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
//This also gets called at build time
export async function getStaticProps({ params }) {
//params contains the post `id`.
//if the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
//Pass post data to the page via props
return { props: { post } }
}
export default Post;
fallback: true
如果fallback
是true
,那么getStaticProps
的行为会有如下变化:
- 由
getStaticPaths
获取的路径会在构建时调用getStaticProps
方法渲染成 HTML 文件; - 在构建时未生成的路径不会以 404 页面返回。相反,当请求不存在的页面路径时,Next.js 会渲染一个当前页面的
回退
(fallback
) 版本。注意,回退
(fallback
) 版本不会提供给像谷歌这样的爬虫程序,而是以阻塞模式呈现路径。 - 在后台,Next.js 会根据请求路经执行
getStaticProps
方法静态生成页面的HTML和JSON。 - 当这些都完成之后,浏览器接收 JSON 数据根据对应的生成路径。这些数据会自动在页面渲染的时候被使用。从用户的角度来看,页面将从备用页面切换到完整页面。
- 在同时,Next.js 将路经添加到预渲染页面列表中。对同一路径的后续请求将渲染已经生成的页面,就像构建时预渲染的其他页面一样。
注意:
fallback: true
在使用next export
时是不被支持的。
- 回退页面(fallback pages)
在页面的回退版本:
- 页面的 props 将是空的。
- 使用
router
,你可以发现如果回退版本被渲染的话,router.isFallback
值会变成true
。
这里有一个使用isFallback
的示例:
//pages/posts/[id].js
import { useRouter } from 'next/router';
function Post({ post }) {
const router = useRouter()
//if the page is not yet generated, this will be displayed initially until
//getStaticProps() finishes running
if (router.isFallback) {
return <div>Loading...</div>
}
// Render post...
}
// This function gets called at build time
export async function getStaticPaths() {
return {
//Only `/posts/1` and `/posts/2` are generated at build time
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
//Enable statically generating additional pages
//For example: `/posts/3`
fallback: true,
}
}
// This also gets called at build time
export async function getStaticProps({ params }) {
//params contains the post `id`.
//if the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params}.id`)
const post = await res.json()
//Pass post data to the page via props
return {
props: { post },
//Re-generate the post at most once per second
//if a request comes in
revalidate: 1,
}
}
export default Post;
-
fallback: true
何时最有用?
当你的应用有大量的依赖于数据(depend on data
)的静态页面fallback: true
,你想要预渲染所有的商品页面,但这样你的构建将花费很长时间。取而代之的是,你可以静态生成一个很小的页面集合,其余部分通过使用fallback: true
生成。当有用户请求一个还没有生成的页面时,用户会看到页面中有一个加载指示器。很快的,getStaticProps
执行完成,页面会根据请求到的数据渲染。之后同样请求此页面的任何用户将会得到静态预渲染的页面。
这确保了用户在保持快速构建和静态生成的好处的同时始终拥有最快的体验。
fallback: true
并不会更新已经生成的页面,具体可查看增量静态再生(Incremental Static Regeneration)。 -
fallback: 'blocking'
如果fallback
是blocking
,getStaticPaths
未返回的新路径将等待HTML完全生成,与 SSR 是完全相同的,然后被缓存下来以供将来的请求使用,因此每个路径只发生一次。
getStaticProps
会有如下行为:
- 由
getStaticPaths
返回的路径将在构建时执行getStaticProps
渲染成HTML。 - 在构建时未生成的路径不会返回一个 404 页面,而是,Next.js 会在第一次请求时执行 SSR 然后返回生成的HTML。
- 当这些执行完之后,浏览器接收生成路径的HTML。从用户的角度来看,它将从”浏览器正在请求页面“转换未”加载完整页面“。没有加载/回退状态闪烁。
- 与此同时,Next.js 将路径添加到预渲染页面的列表。对后续的同一路径的请求将呈现已经生成的页面,就像其他在构建时已经预渲染的页面一样。
fallback: 'blocking'
默认不会更新已经生成的页面。需要更新已经生成的页面,可以结合使用静态增量再生(Incremental Static Regeneration
)和fallback: 'blocking'
。
当使用
next export
的时候fallback: 'blocking'
是不支持的。
- 什么时候使用
getStaticPaths
?
如果要静态预渲染使用动态路由的页面,则应使用getStaticPath。 - 使用 TypeScript:
GetStaticPaths
需要使用TypeScript
,可以引入 next 的GetStaticPaths
类型;示例如下:
import { GetStaticPaths } from 'next'
export const getStaticPaths: GetStaticPaths = async () => {
//...
}
技术细节:
- 结合
getStaicProps
使用
当你的页面中getStaticProps
使用动态路由参数的时候,你必须使用getStaticPaths
。你不能结合getServerSideProps
使用getStaticPaths
。 - 只在构建时在服务端运行
- 只允许在页面中被使用
getStaticPaths
只能被一个页面导出,不能在非页面文件导出。
同时,你必须使用export async function getStaticPaths() {}
,如果你将getStaticPaths
设置为一个页面组件的属性将不会生效。 - 开发环境每次请求都会运行
在开发环境(next dev
),getStaticPaths
将会在每次请求时都执行。
getServerSideProps( 服务端渲染 Server-side Rendering)
当你从一个页面导出一个叫做 getServerSideProps
的 async
函数 ,Next.js 将会使用通过 getServerSideProps
获取的数据在每次请求的时候预渲染这个页面。
export async function getServerSideProps(context) {
return {
props: {}, //will be passed to the page component as props
}
}
context
参数是一个包含以下键的对象:
-
params
:如果一个页面使用动态路由,params
包含路由参数。如果页面名称是[id].js
,然后params
就像{ id: ... }
,要了解更多,可以查看 Dynamic Routing documentation。 -
req
:HTTP 请求消息体。 -
res
:HTTP 响应体。 -
query
:查询对象。 -
preview
:当页面以预览模式(preview mode
)请求时,preview
值为true
,否则为false
。 -
previewData
:值为通过setPreviewData
设置的预览数据。 -
resolvedUrl
:请求URL的规范化版本,为客户端转换去掉 next/data 前缀,并包含原始查询值。 -
locale
:包含当前活跃的路由(如果已启用国际化路由)。 -
locales
:包含所有支持的路由(如果已启用国际化路由)。 -
defaultLocale
:包含配置的默认路由(如果以启用国际化路由)。
getServerSideProps
应该返回一个对象包含以下属性: -
props
:一个可选的对象,该对象会传递给页面组件。值应该是一个序列化对象或者一个返回序列化对象的Promise
。 -
notFound
:一个可选的 Boolean 值,定义一个页面是否要返回 404 状态。
下面是其工作原理的示例:
export async function getServerSideProps(context) {
const res = await fetch(`https://...`);
const data = await res.json()
if (!data) {
return {
notFound: true,
}
}
return {
props: {}, // will be passed to the page component as props
}
}
-
redirect
:一个可选的重定向值,此值允许重定向到一个内部的和外部的资源。它应该形如{ destination: string, permanent: boolean }
。在一些稀有的示例中,你可能需要为较旧的HTTP客户端分配自定义状态代码,以便正确重定向。在这些情况下,可以使用statusCode
属性而不是permanent
属性,但不能同时使用这两个属性。下面是它的工作原理示例:
export aysnc function getServerSideProps(context) {
const res = await fetch(`https://.../data`);
const data = await res.json()
if (!data) {
return {
redirect: {
destination: '/',
permanent: false,
},
}
}
return {
props: {}, //will be passed to the page component as props
}
}
注意:你可以导入顶级作用域中的模块,以便在
getServerSideProps
中使用。getServerSideProps
中使用的导入不会为客户端绑定。这意味着你可以直接在getServerSideProps
中编写服务器端代码。这包括从读写文件系统或数据库。
注意:你不应该在getServerSideProps
中使用fetch()
调用API路由。相反,直接导入API路由中使用的逻辑。对于这种方法,您可能需要稍微重构代码。从外部API获取是很棒的实践。
- 提供
req
中间件
传递给getServerSideProps
的上下文中的req
提供了解析传入请求 (req
)的内置中间件。该中间件是:
-
req.cookies
:包含请求中 cookies 的对象,默认是{}
。
- 使用示例
下面是一个使用getServerSideProps
在请求时获取数据并预渲染的示例。
function Page({ data }) {
//Render data...
}
//This gets called on every request
export async function getServerSideProps() {
//Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
//Pass data to the page via props
return { props: { data } }
}
export default Page
- 何时应该使用?
只有当需要预渲染一个必须在请求时获取数据的页面的时候使用getServerSideProps
。第一个字节到达的时间(TTFB)将会比getStaticProps
慢,因为服务端需要在每次请求时计算结果,并且如果没有额外的配置,CDN无法缓存结果。
如果你不需要预渲染数据,你应该考虑在客户端请求数据。客户端获取数据参考这里 - 使用 TypeScript:
GetServerSideProps
需要使用类型定义(TypeScript
),可以从next
中引入GetServerSideProps
,示例如下:
import { GetServerSideProps } from 'next'
export const getServerSideProps: GetServerSideProps = async (context) => {
//...
}
如果你想为页面的 props
获取推断类型,可以使用 InferGetServerSidePropsType<typeof getServerSideProps>
,示例如下:
import { InferGetServerSidePropsType } from 'next'
type Data = { ... }
export const getServerSideProps = async () => {
const res = await fetch('https://.../data')
const data: Data = await res.json()
return {
props: {
data,
}
}
}
function Page({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
//will resolve posts to type Data
}
export default Page
技术细节:
- 只在服务端运行
getServerSideProps
只在服务端运行,永远不会在浏览器端运行。如果一个页面使用getServerSideProps
,然后:- 当你直接请求这个页面,
getServerSideProps
会直接在请求时运行,页面将会根据返回的数据进行预渲染。 - 当你在客户端请求通过
next/link
ornext/router
请求,Next.js 发送一个API请求向服务端,
- 当你直接请求这个页面,