rerumaccusamus
Version:
The meta-framework suite designed from scratch for frontend-focused modern web development.
206 lines (158 loc) • 5.59 kB
Markdown
---
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 };
};
```
:::note 注
数据同样可以从远端接口获取,此处仅为了演示。
:::
执行 `pnpm run dev`,访问 `http://localhost:8080/api/contacts`:

接下来我们把 `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 接口,调用时无需关心域名、路径等。
:::note 注
在使用 `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 列表,也会重新请求联系人数据:

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