Fetch获取数据
我们知道浏览器中获取数据通常使用FetchAPI(一些古早的系统可能还在使用XHR),但NodeJS中并没有FetchAPI(先不考虑Node v17.5)。NextJS框架则对此做了一些封装,使得SSR组件也能使用Fetch请求数据。除此之外,NextJS还支持为fetch指定缓存策略,根据缓存策略选择动态渲染或是返回静态页面。
服务端组件请求数据
下面例子是一个服务端渲染组件,我们使用FetchAPI请求页面初始化的数据并渲染到页面中。注意服务端组件中请求数据的写法和客户端组件中有些不同,这里我们定义了异步函数,而组件是async的,组件内部直接调用了获取数据的异步函数,这种写法在客户端组件上是不能使用的。
const getStudentList = async () => {
const res = await fetch("http://localhost:8080/api/v1/getStudentList", {
cache: "force-cache",
});
if (!res.ok) {
throw new Error("Failed to fetch data!");
}
return res.json();
};
const Page = async () => {
const studentList = await getStudentList();
return (
<>
<div>
{studentList.map((item, index) => {
return (
<div key={index}>
{item.name} {item.age}
</div>
);
})}
</div>
</>
);
};
export default Page;
我们在fetch()函数中使用了{cache: 'force-cache'}配置,这是NextJS提供的扩展功能,它指定了这个接口请求的缓存策略。force-cache表示使用缓存,它也是fetch()的默认值,可以省略不写。
{cache: 'force-cache'}:只在工程构建时请求接口并将数据缓存(SSG),浏览器请求时数据不会再更新{cache: 'no-store'}:每次浏览器请求时都重新请求接口(SSR){next: { revalidate: 3600 }}:指定3600秒的缓存,缓存过期后会重新请求数据(SSR)
注意:npm run dev开发模式启动项目时,即使设置force-cache每次浏览器刷新也会重新请求接口,但实际上生产运行模式不是这样的!
此外对于Fetch请求的地址,NextJS实现的这个服务端Fetch中我们不能将协议、主机、端口省略不写,在服务端这样写是无法运行的。开发环境、测试环境、生产环境的这些参数肯定是不同的,一种最佳实践是通过环境变量来指定。NextJS中,我们可以用两种方式设置环境变量供程序读取。
一种方式是使用环境变量配置文件,例如.env文件,我们可以在其中对当前工程用到的环境变量进行配置,然后在代码中通过process.env引用。
SSR_FETCH_URL=http://127.0.0.1:8080
或者直接使用命令设置环境变量,以下为Linux操作系统下设置环境变量的示例。
export SSR_FETCH_URL=https://gacfox.com
此时我们就可以使用process.env来读取环境变量了。
const res = await fetch(`${process.env.SSR_FETCH_URL}/api/v1/getStudentList`, {
cache: "force-cache",
});
客户端组件请求数据
客户端组件使用的fetch()还是浏览器原生的FetchAPI,下面代码是纯CSR运行的,异步请求会发生在浏览器和接口服务端之间。
"use client";
import { useEffect, useState } from "react";
const Page = () => {
const [studentList, setStudentList] = useState([]);
const getStudentList = async () => {
const res = await fetch("http://localhost:8080/api/v1/getStudentList");
if (!res.ok) {
throw new Error("Failed to fetch data!");
}
return res.json();
};
useEffect(() => {
getStudentList().then((data) => {
setStudentList(data);
});
}, []);
return (
<>
<div>
{studentList?.map((item, index) => {
return (
<div key={index}>
{item.name} {item.age}
</div>
);
})}
</div>
</>
);
};
export default Page;
客户端组件中使用"use client"标注,我们使用useEffect(() => {}, [])实现在页面加载时请求数据,此处Fetch的地址也可以省略协议、主机和端口(浏览器的Fetch实现支持这么做),例如/api/v1/getStudentList,这种写法学习过React的同学应该已经很熟悉了,这里就不多介绍了。