项目图片

Next.js 的四种渲染方式

weiping8/8/20268088

四种渲染方式分别是:

  • 客户端渲染 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 };
    }
    

客户端渲染 (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 动态填充内容,提供交互能力。