基于Vue3做一套适合自己的状态管理(四)Model的正确的打开方式

416 阅读2分钟

计划章节

  1. 基类:实现辅助功能
  2. 继承:充血实体类
  3. 继承:OptionApi 风格的状态
  4. Model:正确的打开方式
  5. 组合:setup 风格,更灵活
  6. 注册状态的方法、以及局部状态和全局状态
  7. 实践:当前登录用户的状态
  8. 实践:列表页面需要的状态

充血实体类的 Model 要不要来一个?

Vue提供了非常方便的双向绑定的功能(注意这是单向数据流),我们只要再加上几个常用功能,就是一个充血实体类了。

表单需要哪些功能?

我们想想一个表单需要哪些功能?

  • 重置表单 —— 使用$reset()实现
  • 修改前赋值 —— 使用$state 实现
  • 提交 —— 单独做一个函数
  • 数据验证 —— 交给UI库的表单组件

先做个语法糖

/**
 * 给 BaseObject 套个壳,加上 reactive 实现响应性。
 * @param fun 必须使用函数
 * @returns 
 */
export default function createModel<T>(fun: () => T): T & IState {
  const re = new baseObject(fun)
  const ret = reactive(re)
  return ret as  T & IState
}

语法糖有几个目标:

  • 简化代码,实例、套上 reactive 变成一行
  • 组合类型
  • 类型判断

绑定表单设置事件

      <el-form :model="person" label-width="120px" style="width: 500px;">
        <el-form-item label="名称">
          <el-input v-model="person.name" />
        </el-form-item>
        <el-form-item label="年龄">
          <el-input-number v-model="person.age" />
        </el-form-item>
        <el-form-item label="型号">
          <el-select v-model="person.region" placeholder="请选择一个型号">
            <el-option label="大号" value="one" />
            <el-option label="小号" value="two" />
          </el-select>
        </el-form-item>
        <el-form-item label="选择日期">
          <el-col :span="11">
            <el-date-picker
              v-model="person.date1"
              type="date"
              placeholder="选择日期"
              style="width: 100%"
              format="YYYY年MM月DD日"
              value-format="YYYY-MM-DD"
            />
          </el-col>
          <el-col :span="2" class="text-center">
            <span class="text-gray-500">-</span>
          </el-col>
          <el-col :span="11">
            <el-time-picker
              v-model="person.date2"
              placeholder="选择时间"
              style="width: 100%"
              format="HH:mm:ss"
              value-format="HH:mm:ss"
            />
          </el-col>
        </el-form-item>
        <el-form-item label="选择">
          <el-switch v-model="person.switch" />
        </el-form-item>
        <el-form-item label="多选">
          <el-checkbox-group v-model="person.checks">
            <el-checkbox label="Online activities" name="checks" />
            <el-checkbox label="Promotion activities" name="checks" />
            <el-checkbox label="Offline activities" name="checks" />
            <el-checkbox label="Simple brand exposure" name="checks" />
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="单选">
          <el-radio-group v-model="person.radio">
            <el-radio label="Sponsor" />
            <el-radio label="Venue" />
          </el-radio-group>
        </el-form-item>
        <el-form-item label="说明">
          <el-input v-model="person.resource" type="textarea" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="onSubmit">提交</el-button>
          <el-button type="primary" @click="onSubmit2">提交(准备下一条)</el-button>
          <el-button @click="reset" >重置(调用函数)</el-button>
          <el-button @click="person.$reset()" >重置(直接在template里)</el-button>
        </el-form-item>
      </el-form>

使用UI库的 el-form 制作一个表单,绑定数据。重置事件可以直接使用 xxx.$reset()

代码部分

  // 定义一个类型
  type Person = {
    name: string,
    age: number,
    region: string,
    date1: string,
    date2: string,
    switch: boolean,
    checks: string[],
    radio: string,
    resource: string
  }
  
// 创建一个 Model ,类似 reactive 的用法
  const person = Model<Person>(() => {
    return {
      name: 'Model的演示',
      age: 10,
      region: '',
      date1: '',
      date2: '',
      switch: false,
      checks: ['Promotion activities'],
      radio: '',
      resource: ''
    }
  })

  // 重置
  const reset = () => {
    person.$reset()
  }

  // 加载指定的记录,绑定表单
  const load = () => {
    axios.get('./user.html?id=100').then((res: any) => {
      person.$state = res.data
    })
  }

  // 提交表单,然后重置表单
  const onSubmit = () => {
    // 模拟提交
    axios.post('/user.html', person).then(function (res: any) {
        // 重置表单
        person.$reset()
      })
      .catch(function (error: any) {
        console.log(error)
      })
  }

  // 提交表单,然后保留预设值
  const onSubmit2 = () => {
    // 保存需要保留的表单值的副本
    const old = {
      switch: person.switch,
      checks: [...person.checks],
      radio: person.radio
    }

    axios.post('/user.html', person).then(function (res: any) {
        // 重置表单
        person.$reset()
        // 设置需要保留的字段值
        person.$patch(old)
      })
      .catch(function (error: any) {
        console.log(error)
      })
  }
  • 提交的部分没有做处理,直接使用 axios 的基础用法做演示。
  • 创建Model的时候,需要使用函数的方式,因为重置数组需要使用函数的方式。
  • 提交的action没有做到Model内部,感觉没有必要,放在外部更灵活。

小结

感觉似乎没有多大的封装必要,不过嘛,写代码,能省一点就省一点,因为我懒。。。

如果一定要把提交的action做的Model里面的话,那么可以使用OptionState。

源码

gitee.com/naturefw-co…

在线演示

naturefw-code.gitee.io/nf-rollup-s…