
Next.js 的四种渲染方式
四种渲染方式分别是:
- 客户端渲染 CSR (Broswer Side Render) -> 用 JS、Vue、React 创建 HTML
- 静态页面生成 SSG (Static Site Generation) -> 构建时生成页面
- 服务端渲染 SSR (Server Side Render) -> Java(jsp)、c#(asp)等后台的基本功能
- 增量静态再生(ISR) -> 构建时生成页面,但页面内容可以动态更新
服务器端渲染 (SSR)
要对页面使用服务器端渲染,你需要 export 一个名为 getServerSideProps 的 async 函数。服务器将在每次请求时调用此函数。
例如,假设你的页面需要预渲染经常更新的数据(从外部 API 获取)。你可以编写 getServerSideProps 来获取此数据并将其传递给 Page
export default 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 } }; }getServerSideProps 与 getStaticProps 类似,但不同之处在于 getServerSideProps 在每个请求上运行,而不是在构建时运行。
静态站点生成 (SSG)
如果页面使用静态生成,则页面 HTML 是在构建时生成的。这意味着在生产中,页面 HTML 是在运行 next build 时生成的。然后,该 HTML 将在每个请求中重复使用。它可以由 CDN 缓存。
无数据静态生成
function About() { return <div>About</div>; } export default About;带数据的静态生成,有些页面需要获取外部数据进行预渲染。有两种情况,一种或两种情况都可能适用。在每种情况下,你都可以使用 Next.js 提供的以下函数:
- 场景 1:你的页面内容取决于外部数据:使用 getStaticProps。
示例:你的博客页面可能需要从 CMS(内容管理系统)获取博客文章列表。
// TODO: Need to fetch `posts` (by calling some API endpoint) // before this page can be pre-rendered. export default function Blog({ posts }) { return ( <ul> {posts.map((post) => ( <li>{post.title}</li> ))} </ul> ); }- 场景 2:你的页面路径取决于外部数据
Next.js 允许你创建具有动态路由的页面。例如,你可以创建一个名为 pages/posts/[id].js 的文件来显示基于 id 的单个博客文章。这将允许你在访问 posts/1 时显示带有 id: 1 的博客文章。
// 此函数在构建时被调用。 export async function getStaticPaths() { // 调用外部 API 接口来获取帖子 const res = await fetch("https://.../posts"); const posts = await res.json(); // 根据帖子获取我们想要预渲染的路径 const paths = posts.map((post) => ({ params: { id: post.id }, })); // 我们将在构建时仅对这些路径进行预渲染。 // { fallback: false } 这意味着其他路径应返回 404 错误。 return { paths, fallback: false }; }- 场景 1:你的页面内容取决于外部数据:使用 getStaticProps。
客户端渲染 (CSR)
首次加载页面后,导航到同一网站上的其他页面通常会更快,因为只需要获取必要的数据,并且 JavaScript 可以重新渲染页面的部分内容,而无需刷新整个页面。
- 在 Next.js 中,有两种方法可以实现客户端渲染:
- 在页面中使用 React 的 useEffect() 钩子,而不是服务器端渲染方法(getStaticProps 和 getServerSideProps)。
- 使用 SWR 或 TanStack 查询 之类的数据获取库在客户端获取数据(推荐)。
增量静态再生(ISR)
打开一篇博客文章页面,博客的主体内容也许是不变的,但像比如点赞、收藏这些数据总是在变化的吧。使用 SSG 编译成 HTML 文件后,这些数据就无法准确获取了,那可能就退而求其次改为 SSR 或者 CSR 了。
考虑到这种情况,Next.js 提出了 ISR。当用户访问了这个页面,第一次依然是老的 HTML 内容,但是 Next.js 同时静态编译成新的 HTML 文件,当第二次访问或者其他用户访问的时候,就会变成新的 HTML 内容了。
更新静态内容而不重新构建整个站点
通过为大多数请求提供预呈现的静态页面来减少服务器负载
处理大量内容页面,无需长时间 next build
Next.js 支持 ISR,并且使用的方式很简单。只需要在 getStaticProps 中添加一个 revalidate 即可。基于 SSG 的示例代码上进行修改:
// pages/post/[id].js // 保持不变 export default function Blog({ post }) { return ( <> <header>{post.title}</header> <main>{post.body}</main> </> ); } // fallback 的模式改为 'blocking' export async function getStaticPaths() { const res = await fetch('https://jsonplaceholder.typicode.com/posts') const posts = await res.json() const paths = posts.slice(0, 10).map((post) => ({ params: { id: String(post.id) }, })) return { paths, fallback: 'blocking' } } // 使用这种随机的方式模拟数据改变 function getRandomInt(max) { return Math.floor(Math.random() \* max); } // 多返回了 revalidata 属性 export async function getStaticProps({ params }) { const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${getRandomInt(100)}`) const post = await res.json() return { props: { post }, revalidate: 10 } }revalidate 表示当发生请求的时候,至少间隔多少秒才更新页面。
以 revalidate: 10 为例,在初始请求后和接下来的 10 秒内,页面都会使用之前构建的 HTML。10s 后第一个请求发生的时候,依然使用之前编译的 HTML。但 Next.js 会开始构建更新 HTML,从下个请求起就会使用新的 HTML。(如果构建失败了,就还是用之前的,等下次再触发更新)。
当在本地使用 next dev 运行的时候,getStaticProps 会在每次请求的时候被调用。所以如果要测试 ISR 功能,先构建出生产版本,再运行生产服务。
混合渲染
在写 demo 的时候,其实每个页面并没有专门声明使用哪种渲染模式,Next.js 是自动判断的。所以一个 Next.js 应用里支持混合使用多种渲染模式。
- 当页面有 getServerSideProps 的时候,Next.js 切成 SSR 模式。没有 getServerSideProps 则会预渲染页面为静态的 HTML。那可能会问,CSR 呢?就算用 CSR 模式,Next.js 也要提供一个静态的 HTML,所以还是要走预渲染这步的,只不过相比 SSG,渲染的内容少了些。页面可以是 SSG + CSR 的混合,由 SSG 提供初始的静态页面,提高首屏加载速度。CSR 动态填充内容,提供交互能力。