创建 Model

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

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

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 来修改不可变对象,保证了这种写法不会影响对象的不可变性,所以 setValue 仍然是一个纯函数。当然,你也可以直接在 Action 中返回一个新对象,不过这样的写法会更加复杂一些。

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

define 除了接收对象类型的参数,还可以接收函数类型的参数。例如:

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 时,函数内部会自动传入 contextutils 2 个参数,context 是 Reduck 的上下文对象,可以获取到 store 对象,utils 提供了一组工具函数,方便实现 Model 通信等复杂功能需求。

Model 支持复制。例如:

const barModel = fooModel('bar');

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

补充信息

本节涉及的 API 的详细定义,请参考这里