0%

Next.js 入门笔记 | 页面路由、页面数据获取

最近基于 Next.js 写了几个项目,记录一下该框架的使用方法。
Next.js 是一个基于 React 的服务端渲染框架,支持 TypeScript。

本文主要记录 Next.js 的页面路由及页面数据获取。

页面路由

Next.js 的页面路由根据根目录下的 pages 文件夹的文件生成。即一个 page(页面) 就是一个从 .js.jsx.ts.tsx 文件默认导出(export default)的 React 组件,每个 page(页面)都使用其文件名作为路由(route)。

例如:
如果你创建了一个命名为 pages/about.js 的文件并导出一个组件,则可以通过 /about 路径进行访问。

动态路由

如果你创建了一个命名为 pages/posts/[id].js 的文件,那么就可以通过 posts/1posts/2 等类似的路径进行访问。

路由参数获取:

1
2
3
4
5
6
7
8
9
10
import { useRouter } from 'next/router'

const Post = () => {
const router = useRouter()
const { id } = router.query

return <p>Post: {id}</p>
}

export default Post

query 对象除了有 param 的值,还有 query 请求对象的值。
/pages/post/[id].tsx 为例:

router query
/post/1 {"id": "1"}
/post/2?name=steven {"id": "2", "name": "steven"}

如果 paramquery 重名,param 将覆盖 query
/pages/post/[id].tsx 为例:

router query
/post/3?id=steven {"id": "3"}

可以有多级动态路由。
/pages/post/[day]/[name].tsx 为例:

router query
/post/2021-01-01/steven {"day": "2021-01-01", "name": "steven"}

可以用 ... 匹配多级路由。
/pages/post/[...path].tsx 为例:

router query
/post/a {"path": ["a"]}
/post/b/1 {"path": ["b", "1"]}

页面数据获取

Next.js 页面数据获取方法有两种,一是服务端数据获取,一是客户(网页)端异步获取。客户(网页)端异步获取方法和原生 React 框架的常用方法一样,但这种方式在数据爬取的时候往往只能获取页面框架代码,不利于 SEO,所以 Next.js 针对这个问题做了服务端数据获取(服务端渲染)。服务端数据获取的方法是调用 Next.js 暴露的 API,在客户端向前端服务器请求数据后,前端服务器在 Next.js 暴露的 API 对应的生命周期发送数据请求,获取后和网页代码一并返回给客户端。

服务端数据获取

Next.js 暴漏了三个 API 供服务端数据获取—— getStaticProps
getStaticPathsgetServerSideProps

API 触发时间 用途
getStaticProps 构建(build)时 在构建时固定,且后面不更改的数据。
getStaticPaths 构建(build)时 用于动态路由页面,构建时固定,且后面不再更改,需要提供路由列表。
getServerSideProps 向前端服务器请求数据后 根据路由数据向后端数据库请求数据。

getStaticProps

1
2
3
4
5
6
7
8
9
10
11
import { GetStaticProps, InferGetServerSidePropsType } from 'next'

export async function getStaticProps: GetStaticProps(context) {
return {
props: {}, // 传递到页面组件
}
}

export default function Component( componentProps: InferGetServerSidePropsType<typeof getServerSideProps>) {
// getStaticProps 返回值中 props 的值会挂载到 componentProps 上
}

getStaticPaths

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { GetStaticProps, GetStaticPaths, InferGetServerSidePropsType } from "next";

export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [
{ params: { name: "route1" } },
//...
{ params: { name: "routen" } },
],
fallback: false,
};
};

export const getStaticProps: GetStaticProps = async (context) => {
console.log("context.params.name:", context.params.name); // route1 或 route2 ……
return {
props: {}, // 传递到页面组件
};
};

export default function Component( componentProps: InferGetServerSidePropsType<typeof getServerSideProps>) {
// getStaticProps 返回值中 props 的值会挂载到 componentProps 上
}

getServerSideProps

1
2
3
4
5
6
7
8
9
10
11
12
import { GetServerSideProps, InferGetServerSidePropsType } from 'next' //TypeScript:使用 GetServerSideProps

export async function getServerSideProps:GetServerSideProps (context) {
return {
props: {}, // 将作为道具传递到页面组件
}
}

export default function Component( componentProps: InferGetServerSidePropsType<typeof getServerSideProps>) {
// getServerSideProps 返回值中 props 的值会挂载到 componentProps 上
}

客户(网页)端异步获取

1
2
3
4
5
6
7
8
import {useEffect} from 'react';

export default function Component() {
useEffect(() => {
// 异步请求……
}, []);

}

表单 post 请求数据接收

这里再介绍一下表单 post 请求传递数据的方法,这种方法适用于将数据从一个页面发送给另一个页面,不经过后端处理。

当某一个页面向 /post 页面发送表单 post 请求时,下面可实现 /post 页面接收请求数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// pages/api/request.ts

import { GetServerSidePropsContext } from 'next';

type ReqType = GetServerSidePropsContext['req'];

// 该方法旨在接收 post 请求数据
export async function handleRequestBody(req: ReqType) {
let body = '';

for await (const data of req) body += data;

return Object.fromEntries(new URLSearchParams(body));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// pages/post/index.tsx

import { GetServerSideProps, InferGetServerSidePropsType } from 'next';
import { handleRequestBody } from '../api/request';

export const getServerSideProps: GetServerSideProps = async ({
req,
query: { id },
}) => {
const postReq = (await handleRequestBody(req)) || {};
// 此处可根据接收的数据做一些处理……
return {
props: {}, // 返回给渲染组件
};
};

export default function Post( ComponentProps: InferGetServerSidePropsType<typeof getServerSideProps>) {
// getServerSideProps 返回值中 props 的值会挂载到 componentProps 上
}

参考资料