Next.js 是什么?

NextJs 是一款 React 开发框架,主要关注两点:

  • 为生产环境所准备:无需复杂配置即可提供升级环境所需的各种功能
  • 提供良好的开发者体验(DX)

通过 Next.js 框架开发的 React 应用无需配置就可以如服务端渲染SSR、编译时渲染SSG,Typescript 语言支持,自动打包,路由愈加载等功能。

Next.js 功能

我们先看 Next.js 为生产环境 React 应用准备的功能特性

Page 路由

通过 Next.js 初始化 React 应用后,Next.Js 会自动将 pages 目录的每个组件渲染成一个页面,并自动设置好路由:
在 pages 目录新建 about.js

  1. // pages/about.js
  2. export default function About() {
  3. return <div>About: </div>;
  4. }
  1. pages
  2. .
  3. ├── _app.js
  4. ├── about.js
  5. ├── api
  6. └── hello.js
  7. └── index.js

启动应用后,即可以通过 http://localhost:3000/about 访问到 about 页面了。

同时 Next.js 还支持页面的动态路由,比如在 pages 目录建立文件 posts/[id].js,Next.js 就会自动将如 /post/1、/post/2 的请求路由到这个组件上

  1. // pages/posts/[id].js
  2. import { useRouter } from "next/router";
  3. export default function Post() {
  4. const router = useRouter();
  5. const { id } = router.query;
  6. return (
  7. <div>
  8. Post
  9. <div>id: {id}</div>
  10. </div>
  11. );
  12. }

此时,请求如 http://localhost:3000/posts/1http://localhost:3000/posts/2 就会路由到改组件中,同时可以看到,可以通过 router 获取到路由参数

预渲染

出了客户端渲染外,Next.js 支持两种形式的预渲染:1)基于编译时的静态渲染、和2)服务端渲染SSR。在同一个 Next.Js 应用中可以同时混用两种预渲染方式,即部分页面使用静态渲染,部分页面使用 SSR。

静态渲染 SSG

静态渲染,即在编译时,将页面渲染成 HTML 静态文件,这些静态文件可以放到 CDN 上。另外通过 Next.js 静态渲染的页面可以不使用数据,也可以使用数据。
不使用情况的例子,如前文中的 about.js, 在 build 之后可以看到 about.html 中已经渲染的 HTML:

  1. <body>
  2. <div id="__next"><div>About:</div></div>
  3. <script id="__NEXT_DATA__" type="application/json">
  4. {
  5. "props": { "pageProps": {} },
  6. "page": "/about",
  7. "query": {},
  8. "buildId": "Dc_JKg47EiBrN8BG0Me-S",
  9. "nextExport": true,
  10. "autoExport": true,
  11. "isFallback": false,
  12. "scriptLoader": []
  13. }
  14. </script>
  15. </body>

另外,Next.js 的静态渲染是支持使用数据的
这里在 pages 下新建 ssg.js
Next.js 通过函数 getStaticProps 来允许组件在预渲染的时候获取外部数据,比如这里我们通过通过异步请求获取当前时间,然后把时间做为 props 传递给组件

  1. export async function getStaticProps() {
  2. const now = new Date().toLocaleTimeString();
  3. const res = await fetch(`https://postman-echo.com/get?now=${now}`);
  4. const obj = await res.json();
  5. return {
  6. props: {
  7. now: obj.args.now,
  8. },
  9. };
  10. }
  11. export default function SSG({ now }) {
  12. return <div>SSG: {now}</div>;
  13. }

在 build 后,可以看到,生成的 ssg.html 中已经包含了时间:

  1. <body>
  2. <div id="__next">
  3. <div>
  4. SSR:
  5. <!-- -->6:21:06 PM
  6. </div>
  7. </div>
  8. <script id="__NEXT_DATA__" type="application/json">
  9. {
  10. "props": { "pageProps": { "now": "6:21:06 PM" }, "__N_SSG": true },
  11. "page": "/ssg",
  12. "query": {},
  13. "buildId": "PQ7ze9KG5oMk09L5idTqy",
  14. "isFallback": false,
  15. "gsp": true,
  16. "scriptLoader": []
  17. }
  18. </script>
  19. </body>

在使用了动态路由的时候,情况就复杂一点,在预渲染的时候,需要首先确定动态路由的参数。比如上文中的 posts/[id].js 组件,这里我们需要通过 postId 来获取 post 的详情数据,Next.js 提供了 getStaticProps/getStaticPaths 来
getStaticPaths:
通过 getStaticPaths 返回的 paths 数组,Next.Js 可以在编译时确定动态路由的值,即这里每一个 post 的 id 就是一个路由参数 id 的值,这样,在编译的时候,Next.js 通过 getStaticPaths 就可以判断出 posts/[id].js 的所有可能的页面路由

  1. export async function getStaticPaths() {
  2. const res = await fetch(
  3. "https://my-json-server.typicode.com/typicode/demo/posts"
  4. );
  5. const posts = await res.json();
  6. const paths = posts.map((post) => ({ params: { id: `${post.id}` } }));
  7. return {
  8. paths,
  9. fallback: false,
  10. };
  11. }

在具体渲染一个 post 页面时,还需要 getStaticProps 函数来来将获取的数据作为 props 传递给组件

  1. export async function getStaticProps({ params }) {
  2. const res = await fetch(
  3. `https://my-json-server.typicode.com/typicode/demo/posts/${params.id}`
  4. );
  5. const post = await res.json();
  6. return {
  7. props: { post },
  8. };
  9. }

最后是组件的实现

  1. export default function Post({ post }) {
  2. return (
  3. <div>
  4. Post
  5. <div>id: {post.id}</div>
  6. <div>title: {post.title}</div>
  7. </div>
  8. );
  9. }

服务端渲染 SSR

Page 页面也可以选择使用 SSR 来做预渲染,通过在 page 组件中实现 getServerSideProps 即可:

  1. // pages/status.js
  2. export async function getServerSideProps() {
  3. const now = new Date().toLocaleTimeString();
  4. const res = await fetch(`https://postman-echo.com/get?now=${now}`);
  5. const obj = await res.json();
  6. return {
  7. props: {
  8. now: obj.args.now,
  9. },
  10. };
  11. }
  12. export default function Status({ now }) {
  13. return <div>Status: {now}</div>;
  14. }

可以对比上文的 ssg 页面和这里的 ssr 页面,可以看到,ssg 页面在多次请求刷新前后显示的时间不会变化,而 ssr 页面在每次刷新都会显示新的时间。

Hybird 混合端渲染

Next.js 支持部分SSG和SSR的混合使用,即部分页面使用 SSG、部分页面使用 SSR。

API 路由

如 Pages 功能一样,Next.js 支持服务端 API 的自动支持,在 Next.js 项目中,在 pages/api 目录下每一个文件会自动注册成功一个 API 访问点

  1. // pages/api/user.js
  2. export default function handler(req, res) {
  3. res.status(200).json({ name: 'John Doe' })
  4. }

启动就可以通过 /api/user 来请求这个接口了

总结

总体来说,Next.js 可以说是 create-react-app 的一个进阶版本,即它除了帮你快速的创建 React 应用外,还支持很多无需复杂配置的功能,包括路由和各种场景的预渲染,来满足生产环境所需。