【入门级】创建你的第一个 Next.js 服务端渲染(SSR)应用!

1,659 阅读5分钟

创建应用

一行代码,即可开启 Next.js 应用,

npx create-next-app

之后,终端会打印如下信息,表明将会安装 reactreact-domnext 三个依赖包,其中的 next.js 就是 React 的生产环境框架,它能让你快速在生产环境中使用 React,而不必再额外去担心路由、编译选项、数据缓存等问题。

image.png

下面我们进入目应用,开始体验一些 Next.js 自带的特性。

一、约定式路由

Next.js 在根目录的 pages 文件夹下的文件路径即是网页的路由,举个例子,我们在 pages/ 目录下新建 /path1, /path2 两个文件夹,里面各自放置一个 index.jsx 文件,然后写一个 React 组件,

// path1/index.jsx
export default function Comp1() {
  return <div>path1</div>;
}

// path2/index.jsx
export default function Comp2() {
  return <div>path2</div>;
}

只需如此,在运行 yarn dev 后,next 项目会跑在 localhost:3000 端口,我们访问这个应用下的path1path2两个路径,就可以看到,

image.png

image.png

组件已经被渲染到了相应的路由下!这就不用借助 react-router 来做路由啦,很是方便!

二、pre-rendering(预渲染)

在 Next.js 的语境下,预渲染的一定是带有依赖外部数据的组件,数据通过 api 去 fetch 到,这些数据通常储存在数据库里,而且会作为网页的内容的呈现。之所以需要预渲染这些网页,是因为如果等到网站在客户端(client)的浏览器上加载好了,再去获取,就会显得有比较高的延迟(因为要等待这些数据的加载)。预渲染是很重要的Web性能优化手段。

Next.js 提供两种预渲染的方式,一种是静态生成(Static Generation),一种是服务端渲染(Server Side Rendering,SSR),它们的区别是,

静态生成(推荐):HTML在构建时生成,并在每次请求时重用。

服务器端渲染:每个请求都会生成HTML。

对于服务器处理能力有限的小站点,静态生成是更好的方式,因为它减轻了服务器的压力。

我们通过一个博客网页的例子,来看看在 Next.js 中如何让组件的数据静态生成,

静态生成(Static Generation)

我们先新建一个 Blog 组件,

// pages/blog/index.jsx
export default function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

这个 Blog 依赖外部的 posts 参数来生成渲染的内容,这个 posts 是通过某个 api 来获取的,我们可以在 blog/index.jsx 文件中同时导出一个 getStaticProps 函数,如下,

import * as fs from "fs";
import { join } from "path";

export async function getStaticProps() {
  // 读根目录下的 _posts/post1.json 文件
  const post1 = fs.readFileSync(
    join(process.cwd(), "_posts/post1.json"), "utf8"
  );
  // 读根目录下的 _posts/post2.json 文件
  const post2 = fs.readFileSync(
    join(process.cwd(), "_posts/post2.json"), "utf8"
  );

  return {
    props: {
      posts: [JSON.parse(post1), JSON.parse(post2)],
    },
  };
}

// Blog 组件
export default function Blog({ posts }) { ... }

通常情况下,getStaticProps的数据来源要么是通过 fs 本地读取的,要么是 fetch 获取外部数据(比如后端的api,数据库)。

现在,我们访问 localhost:3000/blog 就可以看到 json 文件里的数据啦,

image.png

这就是静态生成(Static Generation)的效果,需要注意,getStaticProps 的数据只在 build 的时候获取一次,因此只适合用来获取那些长期不会改变的数据(或者每次改变都确定会重新构建一次)。如果要获取那些频繁动态变化的数据,则需要用到服务端渲染(SSR),下面我们就来看看吧。

服务端渲染(SSR)

如果你已经熟读了上面的部分,那么使用SSR很简单,只需要把导出的函数替换为 getServerSideProps 即可。

举个例子,依旧是 Blog 组件,我们这次使用 axios 来获取掘金的两篇文章,如下,

// pages/blogssr/index.jsx
import axios from "axios";

// 从 html 字符串中提取 title
function getTitle(str) {
  return /<title>.*<\/title>/
    .exec(str)[0]
    .replace("<title>", "")
    .replace("</title>", "");
}

export async function getServerSideProps() {
  // 访问两篇掘金文章的地址,提取 title
  const title1 = await axios
    .get("https://juejin.cn/post/7094651577117442056")
    .then((res) => getTitle(res.data));
  const title2 = await axios
    .get("https://juejin.cn/post/7093061981254451231")
    .then((res) => getTitle(res.data));
  
  // 返回结果
  return {
    props: {
      posts: [title1, title2],
    },
  };
}

// Blog 组件
export default function Blog({ posts }) {

  return (
    <ul>
      {posts.map((post) => (
        <li key={post}>{post}</li>
      ))}
    </ul>
  );

然后访问该页面,

image.png

可以看到,两篇文章的标题就被打印出来了~

注意,我们可以通过观察网络请求发现,返回的 html 文本本身就带上了这两个标题,

image.png

这说明外部数据时服务端渲染好的,而不是到了客户端才发送获取数据的请求,这种情况下,在服务器处理能力足够强悍时,页面的加载速度是非常快的。

另外,SSR 可以通过参数来配置缓存逻辑,详情可以参考 🔗官方文档

Static Generation 对比 SSR

用 Next.js 官方的话来说就是,

你应该问问自己:“我能在用户请求之前预先获取(pre-render)这个页面吗?”如果答案是肯定的,那么你应该选择静态生成。

否则的话,对于频繁更新的数据,应该使用 CSR 或者 SSR,

客户端渲染(CSR):预请求时跳过页面的某些部分,然后使用客户端JavaScript请求来填充它们。

服务端渲染(SSR):Next.js会根据每个请求预先渲染一个页面。由于CDN无法缓存页面,因此速度会变慢,但页面始终是最新的。

实际上,我们的 React 项目在大多数情况下都使用 CSR,通过请求过程中的 loading 态来优化用户感知。

三、结尾

好了,这样我们就很快用 Next 搭建了一个 SSR 的服务端项目,是不是很简单呢?Next.JS 还有很多有趣,厉害的特性,包括但不限于,

  • 使用 swc (基于 Rust)来快速开发和构建应用
  • 支持 React Server Component (服务器端组件)
  • 内置的图片加载优化(Image Gallery with Supabase)
  • useSWR(stale-while-revalidate)数据请求和缓存方案

一个宝可梦图片的例子:🔗网址

如果你还对某个特性感兴趣的话,欢迎在评论区留言讨论~