---
title: 从 BFF 获取数据
---

上一小节中，我们创建了【 函数写法 】的 BFF，这一小节中，我们将用该模式为联系人列表实现一个数据接口。

我们首先把不需要的样板文件清理掉，把 `index.ts` 重命名为 `contacts.ts`，执行以下命令：

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<Tabs>
<TabItem value="macOS" label="macOS" default>

```bash
rm -r api/info api/_app.ts
mv api/index.ts api/contacts.ts
```

</TabItem>
<TabItem value="Windows" label="Windows">

```powershell
rm -r api/info
rm api/_app.ts
mv api/index.ts api/contacts.ts
```

</TabItem>
</Tabs>


此时 API 路由将变为 `/api/contacts`。

我们使用 [faker](https://github.com/Marak/Faker.js) 来 mock 需要的数据，首先安装依赖：

```bash
pnpm add faker
```

将 `api/contacts.ts` 内容改为：

```js
import { name, internet } from 'faker';

export const get = async () => {
  const mockData = new Array(20).fill(0).map(() => {
    const firstName = name.firstName();
    return {
      name: firstName,
      avatar: `https://avatars.dicebear.com/api/identicon/${firstName}.svg`,
      email: internet.email(),
    };
  });

  return { code: 200, data: mockData };
};
```

:::info 注
数据同样可以从远端接口获取，此处仅为了演示。
:::

执行 `pnpm run dev`，访问 `http://localhost:8080/api/contacts`：

![browser-result](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/08/api-result.png)

接下来我们把 `src/contacts/App.tsx` 里硬编码的 `mockData` 改成从 BFF 加载。

我们将业务逻辑迁移到组件中，`App.tsx` 只保留简单的路由部分，执行：

<Tabs>
<TabItem value="macOS" label="macOS" default>

```bash
mkdir -p src/contacts/components/AllContacts/
mkdir -p src/contacts/components/ArchivedContacts/
touch src/contacts/components/AllContacts/index.tsx
touch src/contacts/components/ArchivedContacts/index.tsx
```

</TabItem>
<TabItem value="Windows" label="Windows">

```powershell
mkdir -p src/contacts/components/AllContacts/
mkdir -p src/contacts/components/ArchivedContacts/
ni src/contacts/components/AllContacts/index.tsx
ni src/contacts/components/ArchivedContacts/index.tsx
```

</TabItem>
</Tabs>

`ArchivedContacts/index.tsx` 的内容：

```js
import { List } from 'antd';
import Item from '../Item';

const getAvatar = (users: Array<{ name: string; email: string }>) =>
  users.map(user => ({
    ...user,
    avatar: `https://avatars.dicebear.com/v2/identicon/${user.name}.svg`,
  }));

const mockArchivedData = getAvatar([
  { name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
  { name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
]);

const ArchivedContacts = () => (
  <List
    dataSource={mockArchivedData}
    renderItem={info => <Item key={info.name} info={info} />}
  />
);

export default ArchivedContacts;
```

`AllContacts/index.tsx` 的内容：

```tsx
import { useState, useEffect } from 'react';
import { List } from 'antd';
import { get as concats } from '@api/contacts';
import Item from '../Item';

const AllContacts = () => {
  const [list, setList] = useState(
    [] as Array<{ name: string; email: string; avatar: string }>,
  );
  const loadMockData = async () => {
    const { data } = await concats();
    setList(data);
  };
  useEffect(() => {
    if (!list.length) {
      loadMockData();
    }
  });
  return (
    (list.length && (
      <List
        dataSource={list}
        renderItem={info => <Item key={info.name} info={info} />}
      />
    )) || (
      <div className="p-4 items-center border-gray-200 border-b border-t custom-text-gray">
        Pending...
      </div>
    )
  );
};

export default AllContacts;
```

在 Modern.js 中，可以通过调用函数（前后端一体化）的方式，直接调用 BFF 接口，调用时无需关心域名、路径等。

:::info 注
在使用 `pnpm run new` 创建 BFF 时，Modern.js 已经默认在 `tsconig.json` 中注入了别名。这也是之前提到的，生成器在项目创建之后并不会被抛弃，仍旧可以在开发过程中不断为项目提供新的内容。
:::

接下来修改 `src/contacts/App.tsx`，把 `List` 组件的调用代码替换成上面的 `AllContacts` 和 `ArchivedContacts`：

```tsx
<Route path="/" exact={true}>
  <Helmet>
    <title>All</title>
  </Helmet>
  <AllContacts />
</Route>
<Route path="/archives" exact={true}>
  <Helmet>
    <title>Archives</title>
  </Helmet>
  <ArchivedContacts />
</Route>
```

删除 `App.tsx` 里的 mock 数据和 `List` 组件，把 `Item` 组件替换成 `ContactList`：

```tsx
import { useState } from 'react';
import { Radio, RadioChangeEvent } from 'antd';
import { Route, Switch, useHistory } from '@modern-js/runtime/router';
import { Helmet } from '@modern-js/runtime/head';
import 'tailwindcss/base.css';
import 'tailwindcss/components.css';
import 'tailwindcss/utilities.css';
import './styles/utils.css';
import AllContacts from './components/AllContacts';
import ArchivedContacts from './components/ArchivedContacts';
```

执行 `pnpm run dev`，再打开页面`http://localhost:8000/contacts`，可以看到页面加载后，会先初始化联系人数据（显示 pending），之后每次切换到 All 列表，也会重新请求联系人数据：

![browser-result](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/08/browser-result.png)

---

> 本小节的代码可以在[这里查看](https://github.com/modern-js-dev/modern-js-examples/tree/main/tutorials/c09/hello-modern-3)。

