---
sidebar_position: 2
title: 创建 Model
---
# 创建 Model

上一节的计数器应用中，我们简单演示了如何创建一个 Model。本节我们将详细介绍 Model 的创建方法。

通过 `model` API 创建 Model，例如，`model('foo')` 表示要创建一个名为 `foo` 的 Model，通过调用 `model('foo')` 返回的 `define` 函数，定义 Model 包含的 State、Actions 等：

```js
import { model, useModel } from '@modern-js/runtime/model';

const fooModel = model('foo').define({
  state: {
    value: 'foo',
  },
  actions: {
    setValue: (state, payload){
      state.value = payload
    }
  }
});
```

:::info
- Model 中的 Action 中不能包含有副作用的逻辑，如请求 HTTP 接口，访问 localStorage 等。
- `setValue` 内部直接修改了入参 State，看起来是违反了纯函数的定义，实际上 Reduck 内部使用 [immer](https://github.com/immerjs/immer) 来修改不可变对象，保证了这种写法不会影响对象的不可变性，所以 `setValue` 仍然是一个纯函数。当然，你也可以直接在 Action 中返回一个新对象，不过这样的写法会更加复杂一些。

:::

`define` 接收的参数，只是对 Model 原始结构的描述：内部定义的 State、Actions 等。`define` 返回值 `fooModel` 才是真正创建得到的 Model 对象。例如，虽然 `setValue`，有 2 个参数，但是当真正调用 `setValue` 这个 Action 时，我们只需要传入 `payload` 一个参数，因为我们调用的是 `fooModel` 上的 Action 方法，而不是 Model 原始结构上描述的 Action 方法。详细参考[使用 Model](/guides/topic-detail/model/use-model)。

`define` 除了接收对象类型的参数，还可以接收函数类型的参数。例如：

```js
import { model, useModel } from '@modern-js/runtime/model';

const fooModel = model('foo').define((context, utils) => {
  return {
    state: {
      value: 'foo',
    },
    actions: {
      setValue: (state, payload){
        state.value = payload
      }
    }
  }
});
```

通过函数定义 Model 时，函数内部会自动传入 `context`、`utils` 2 个参数，`context` 是 Reduck 的上下文对象，可以获取到 `store` 对象，`utils` 提供了一组工具函数，方便实现 Model 通信等复杂功能需求。

Model 支持复制。例如：

```ts
const barModel = fooModel('bar');
```

barModel 是基于 fooModel 创建出一个的新的 Model 对象，类比面向对象编程语言中的概念，barModel 和 fooModel 是基于同一个类（Class）创建出的 2 个实例对象。当两个模块的状态管理逻辑相同，例如一个页面中的两个 tab 模块，使用的数据的结构和逻辑相同，区别只是从不同的接口获取数据，那么可以通过 Model 复制的方式，创建 2 个不同的 Model 对象。

:::info 补充信息
本节涉及的 API 的详细定义，请参考[这里](/apis/app/runtime/model/model_)。

:::
