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的同学应该已经很熟悉了,这里就不多介绍了。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。