本文档适用于 Next.js 9.3 或更高版本。 如果你使用的是较旧版本的 Next.js,请参阅 先前版本的文档。
在 Next.js 中,一个 page(页面) 就是一个从 .js
、jsx
、.ts
或 .tsx
文件导出(export)的 React 组件 ,这些文件存放在 pages
目录下。每个 page(页面)都使用其文件名作为路由(route)。
示例: 如果你创建了一个命名为 pages/about.js
的文件并导出(export)一个如下所示的 React 组件,则可以通过 /about
路径进行访问。
function About() {
return <div>About</div>
}
export default About
Next.js 支持具有动态路由的 pages(页面)。例如,如果你创建了一个命名为 pages/posts/[id].js
的文件,那么就可以通过 posts/1
、posts/2
等类似的路径进行访问。
要了解有关动态路由的更多信息,请查看 动态路由文档。
默认情况下,Next.js 将 预渲染 每个 page(页面)。这意味着 Next.js 会预先为每个页面生成 HTML 文件,而不是由客户端 JavaScript 来完成。预渲染可以带来更好的性能和 SEO 效果。
每个生成的 HTML 文件都与该页面所需的最少 JavaScript 代码相关联。当浏览器加载一个 page(页面)时,其 JavaScript 代码将运行并使页面完全具有交互性。(此过程称为 水合(hydration)。)
Next.js 具有两种形式的预渲染: 静态生成(Static Generation) 和 服务器端渲染(Server-side Rendering)。这两种方式的不同之处在于为 page(页面)生成 HTML 页面的 时机 。
重要的是,Next.js 允许你为每个页面 选择 预渲染的方式。你可以创建一个 “混合渲染” 的 Next.js 应用程序:对大多数页面使用“静态生成”,同时对其它页面使用“服务器端渲染”。
出于性能考虑,相对服务器端渲染,我们更 推荐 使用 静态生成 。 CDN 可以在没有额外配置的情况下缓存静态生成的页面以提高性能。但是,在某些情况下,服务器端渲染可能是唯一的选择。
你还可以将 客户端渲染 与静态生成或服务器端渲染一起使用。这意味着页面的某些部分可以完全由客户端 JavaScript 呈现。要了解更多信息,请查看 数据获取 章节的文档。
如果一个页面使用了 静态生成,在 构建时(build time) 将生成此页面对应的 HTML 文件 。这意味着在生产环境中,运行 next build
时将生成该页面对应的 HTML 文件。然后,此 HTML 文件将在每个页面请求时被重用,还可以被 CDN 缓存。
在 Next.js 中,你可以静态生成 带有或不带有数据 的页面。接下来我们分别看看这两种情况。
默认情况下,Next.js 使用 “静态生成” 来预渲染页面但不涉及获取数据。如下例所示:
function About() {
return <div>About</div>
}
export default About
请注意,此页面在预渲染时不需要获取任何外部数据。在这种情况下,Next.js 只需在构建时为每个页面生成一个 HTML 文件即可。
某些页面需要获取外部数据以进行预渲染。有两种情况,一种或两种都可能适用。在每种情况下,您都可以使用 Next.js 提供的特殊功能:
getStaticProps
。getStaticPaths
(通常还要同时使用 getStaticProps
)。例如: 您的博客页面可能需要从 CMS(内容管理系统)中获取博客文章列表。
// TODO: 需要获取 `posts`(通过调用 API )
// 在此页面被预渲染之前
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
export default Blog
要在预渲染时获取此数据,Next.js 允许你从同一文件 export(导出)
一个名为 getStaticProps
的 async(异步)
函数。该函数在构建时被调用,并允许你在预渲染时将获取的数据作为 props
参数传递给页面。
function Blog({ posts }) {
// Render posts...
}
// 此函数在构建时被调用
export async function getStaticProps() {
// 调用外部 API 获取博文列表
const res = await fetch('https://.../posts')
const posts = await res.json()
// 通过返回 { props: posts } 对象,Blog 模块
// 在构建时将接收到 `posts` 参数
return {
props: {
posts,
},
}
}
export default Blog
要了解有关 getStaticProps
工作原理的更多信息,请查看 获取数据 章节的文档。
Next.js 允许你创建具有 动态路由 的页面。例如,你可以创建一个名为 pages/posts/[id].js
的文件用以展示以 id
标识的单篇博客文章。当你访问 posts/1
路径时将展示 id: 1
的博客文章。
要了解有关动态路由的更多信息,请查看动态路由章节的文档。
但是,在构建 id
所对应的内容时可能需要从外部获取数据。
例如: 假设你只向数据库添加了一篇博客文章(标记为 id: 1
)。这种情况下,你只想在构建时针对 posts/1
进行预渲染。
稍后,你又添加了第二篇文章,标记为 id: 2
。这是,你希望对 posts/2
也进行预渲染。
因此,预渲染的页面 paths(路径) 取决于外部数据。为了解决这个问题,Next.js 允许你从动态页面(在这里是 pages/posts/[id].js
)中 export(导出)
一个名为 getStaticPaths
的 async(异步)
函数。该函数在构建时被调用,并允许你指定要预渲染的路径。
// 此函数在构建时被调用
export async function getStaticPaths() {
// 调用外部 API 获取博文列表
const res = await fetch('https://.../posts')
const posts = await res.json()
// 根据博文列表生成所有需要预渲染的路径
const paths = posts.map((post) => `/posts/${post.id}`)
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
同样在 pages/posts/[id].js
中,你还需要export(导出) getStaticProps
以便可以获取 id
所对应的博客文章的数据并进行预渲染:
function Post({ post }) {
// Render post...
}
export async function getStaticPaths() {
// ...
}
// 在构建时也会被调用
export async function getStaticProps({ params }) {
// params 包含此片博文的 `id` 信息。
// 如果路由是 /posts/1,那么 params.id 就是 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// 通过 props 参数向页面传递博文的数据
return { props: { post } }
}
export default Post
要了解有关 getStaticPaths
工作原理的更多信息,请查看 获取数据 章节的文档。
我们建议您尽可能使用 静态生成 (带有或不带数据),因为你的所有 page(页面)都可以只构建一次并托管到 CDN 上,这比让服务器根据每个页面请求来渲染页面快得多。
还可以对多种类型的页面使用“静态生成”,包括:
您应该问问自己:“我可以在用户请求之前预先渲染此页面吗?” 如果答案是肯定的,则应选择“静态生成”。
另一方面,如果你无法在用户请求之前预渲染页面,则“静态生成” 不是 一个好主意。这也许是因为你的页面需要显示频繁更新的数据,并且页面内容会随着每个请求而变化。
在这种情况下,您可以执行以下任一操作:
也被称为 “SSR” 或 “动态渲染”。
如果 page(页面)使用的是 服务器端渲染,则会在 每次页面请求时 重新生成页面的 HTML 。
要对 page(页面)使用服务器端渲染,你需要 export
一个名为 getServerSideProps
的 async
函数。服务器将在每次页面请求时调用此函数。
例如,假设你的某个页面需要预渲染频繁更新的数据(从外部 API 获取)。你就可以编写 getServerSideProps
获取该数据并将其传递给 Page
,如下所示:
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
类似于 getStaticProps
,但两者的区别在于 getServerSideProps
在每次页面请求时都会运行,而在构建时不运行。
要了解有关 getServerSideProps
的工作原理的更多信息,请查看我们的 获取数据文档
我们已经讨论了 Next.js 的两种预渲染形式。
getStaticProps
函数(如果需要还可以导出 getStaticPaths
函数)。对于可以在用户请求之前预先渲染的页面来说,这非常有用。你也可以将其与客户端渲染一起使用以便引入其他数据。getServerSideProps
函数。由于服务器端渲染会导致性能比“静态生成”慢,因此仅在绝对必要时才使用此功能。我们建议你接下来阅读以下章节: