---
title: 容器组件（Container）
---

前两个章节中，我们把项目中的业务逻辑拆分成了两个 layer，一个是【 视图组件 】，另一个是 【 业务模块 】。

【 视图组件 】 负责 UI 展示、交互等；【 业务模块 】负责实现 UI 无关的业务逻辑，专门管理状态，既可以是组件状态（局部，不唯一），也可以是应用状态（全局，唯一）。

像 `components/Contacts/index.tsx` 这样使用了 `useModel` API 的组件，其实已经在**客户端应用架构**中扮演一种新的角色，负责把 View 和 Model 这两个 layer 连接起来，类似传统 MVC 架构中 Controller 的角色，也类似一种 ViewController。

因为这种组件属于一种新的功能模块，在 Modern.js 里我们沿用习惯，把它们称作【 容器组件（Container）】。

容器组件推荐放在专门的 `containers/` 目录里，我们执行以下命令：

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

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

```bash
mkdir -p src/contacts/containers/
mv src/contacts/components/Contacts/index.tsx src/contacts/containers/Contacts.tsx
rm -r src/contacts/components/Contacts/
```

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

```powershell
mkdir -p src/contacts/containers/
mv src/contacts/components/Contacts/index.tsx src/contacts/containers/Contacts.tsx
rm -r src/contacts/components/Contacts/
```

</TabItem>
</Tabs>

修改 `containers/Contacts.tsx` 的代码：

```tsx
import Item from '../components/Item';
import contacts from '../models/contacts';
```

修改 `App.tsx` 的代码：

```tsx
import Contacts from './containers/Contacts';
```

重构完成，现在的项目结构是：

```md
.
├── .vscode/
├── api/
│   ├── .eslintrc.json
│   └── contacts.ts
├── src/
│   ├── contacts/
│   │   ├── components/
│   │   │   ├── Avatar/
│   │   │   │   ├── index.stories.tsx
│   │   │   │   └── index.tsx
│   │   │   └── Item/
│   │   │       ├── index.test.tsx
│   │   │       └── index.tsx
│   │   ├── containers/
│   │   │   └── Contacts.tsx
│   │   ├── models/
│   │   │   ├── contacts.test.ts
│   │   │   └── contacts.ts
│   │   ├── styles/
│   │   │   └── utils.css
│   │   ├── App.css
│   │   └── App.tsx
│   ├── landing-page/
│   │   └── pages/
│   │       ├── comments/
│   │       │   └── [commentTitle]/
│   │       │       └── index.tsx
│   │       ├── _app.tsx
│   │       ├── docs.tsx
│   │       └── index.tsx
│   ├── .eslintrc.json
│   └── modern-app-env.d.ts
├── .editorconfig
├── .gitignore
├── .npmrc
├── .nvmrc
├── README.md
├── package.json
├── pnpm-lock.yaml
└── tsconfig.json
```

`components/` 里的【 视图组件 】，都是目录形式，如 `Avatar/index.tsx`。而 `containers/` 里的【 容器组件 】，都是单文件形式，如 `contacts.tsx`。**这是我们推荐的一种最佳实践**。

在​ [添加 UI 组件（Component）](../c06-css-and-component/6.1-css-in-js.md) 章节提到过，【 视图组件 】用目录形式，是因为【 视图组件 】负责实现 UI 展示和交互细节，可以演变的复杂，用目录形式，可以方便增加子文件，包括专用的资源（图片等）、专用子组件、CSS 文件等，在这个目录内部可以随意重构，只考虑最小局部。

而【 容器组件 】只负责连接，是一个胶水层，复杂的业务逻辑和实现细节都交给 View 层和 Model 层去实现，【 容器组件 】自己应该保持简单清晰，不应该包含复杂实现细节，所以不应该有内部结构，采用单文件形式不但更简洁，也能起到约束作用，提醒开发者不要把容器组件写复杂。

---

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