Live2d Test Env

v3中reactive中的小坑

“现在我们能造什么?能造桌子椅子,能造茶碗茶壶,能种粮食,还能磨成面粉,还能造纸,但是,一辆汽车、一架飞机、一辆坦克、一辆拖拉机都不能造。”

前两天在写一个比较痛苦的表单,大概是这种: 抽屉内的表单在点击不同radio时会显示不同的待选项,抽屉关闭时所有项要回到初始态。保存时要分别校验不同项下的内容,基础校验是不能为空。
也同之前一样在使用vue3的ref

小坑:关于reactive
const formData = ref({
  name: ``,
  type: 0,
  // files: [],
  'base-text-diff': {
    baseFileOnly: []
  },
  
  'model-text-diff': {
    modelFileOnly: [], //单
    diffedFileList: []
  },
  'appoint-text-diff': {
    specialList: [], //特定页文件
    beforePointFileOnly: [], //特定页,指定前
    afterPointFileOnly: [] //特定页,指定后
  },
  pointPageType: `specialPage`, // 特定页
  diffPageType: [], // 比对页:目录封面印章
  // 范文页码
  specialPage1_start: 1,
  specialPage1_end: 1,
  // 比对页码
  specialPage2_start: 1,
  specialPage2_end: 1
})

由于表单太过复杂,且判断较多,于是摒弃了使用ui的表单校验方式,改为手动重置:

//  当修改任务类型后,重置文件列表
watch(
  () => formData.value.type,
  (n, o) => {
    resetTaskType(n, o)
    resetFileList()
  }
)
const resetTaskType = (newval, oldval) => {
  // console.log('oldval :>> ', oldval)
  if (oldval === 2) {
    formData.value.pointPageType = `specialPage`
    formData.value.diffPageType = []
    formData.value.specialPage1_start = 1
    formData.value.specialPage1_end = 1
    formData.value.specialPage2_start = 1
    formData.value.specialPage2_end = 1
  }
  resetFileList()
}
// 重置所有绑定的文件数据
const resetFileList = () => {
  formData.value.['base-text-diff'].baseFileOnly = []
  formData.value.['model-text-diff'].modelFileOnly = []
  formData.value.['model-text-diff'].diffedFileList = []
  formData.value.['appoint-text-diff'].specialList = []
  formData.value.['appoint-text-diff'].beforePointFileOnly = []
  formData.value.['appoint-text-diff'].afterPointFileOnly = []
}

原本以为这样就ok了,毕竟虽然麻烦但很简单。但是发现它报错了,没有截图,总之就是报了个奇怪的错误

后来就尝试使用reactive声明变量。

第一次有机会使用,说不激动是不可能的,但是在我看来也就仅仅是将value去掉而已,但是在重置此变量时告警:reactive声明的是常量,无法更改
欺天啦!我声明的是const不假 但是reactive不是用来声明响应式变量的吗??
退一步越想越气,于是翻了翻官方文档:

那该怎么做?
后来翻了翻解决方案,发现一般都是使用此方案:

// 本质上是遍历初始数据后将初始数据重新赋予reactive数据
声明初始化数据:
const baseForm = () => ({
  name: ``,
  type: 0,
  // files: [],
  
  'base-text-diff': {
    baseFileOnly: []
  },
 
  'model-text-diff': {
    modelFileOnly: [], //单
    diffedFileList: []
  },
  'appoint-text-diff': {
    specialList: [], //特定页文件
    beforePointFileOnly: [], //特定页,指定前
    afterPointFileOnly: [] //特定页,指定后
  },
  pointPageType: `specialPage`, // 特定页
  diffPageType: [], // 比对页:目录封面印章
  // 范文页码
  specialPage1_start: 1,
  specialPage1_end: 1,
  // 比对页码
  specialPage2_start: 1,
  specialPage2_end: 1
})
//声明reactive变量:
const formData = reactive(baseForm())
// 重置reactive变量
const resetFormData = () => {
  Object.keys(baseForm()).forEach((key) => {
    formData[key] = baseForm()[key]
  })
}
如何校验非表单数据?

上面说摒弃了校验表单,因而要自己实现校验功能,由于不同类型要有不同的校验数据,因而有了以下方式 :

// 点击抽屉的确认按钮
const confirm = () => {
  const checkError = valiteForm()
}
// 校验
const valiteForm = () => {
  if (!formData.name) {
    return { msg: `请输入用户名称` }
  }
  switch (formData.type) {
    // 文本 对比
    case 0:
      if (!formData['base-text-diff'].baseFileOnly.length) {
        return { msg: `未上传文本比对类型文件,请确认` }
      }
      break
    case 1:
      if (
        !formData['model-text-diff'].modelFileOnly.length ||
        !formData['model-text-diff'].diffedFileList.length
      ) {
        const msgType = !formData['model-text-diff'].modelFileOnly.length ? `范文` : `比对`
        return { msg: `未上传范本比对类型的${msgType}文件,请确认` }
      }
      break
    // 指定页
    case 2:
      if (formData.pointPageType === `specialPage`) {
        if (!formData.diffPageType.length) {
          return { msg: `未选择特定页比对中的 比对页,请确认` }
        }
        if (!formData['appoint-text-diff'].specialList.length) {
          return { msg: `未上传特定页比对中的比对文件,请确认` }
        }
      }
      if (formData.pointPageType === 'pointPageNum') {
        if (formData.specialPage1_end < formData.specialPage1_start) {
          return { msg: `特定页比对中的范文页页码有误,请确认` }
        }
        if (formData.specialPage2_end < formData.specialPage2_start) {
          return { msg: `特定页比对中的比对页页码有误,请确认` }
        }
        if (
          !formData[`appoint-text-diff`].beforePointFileOnly.length ||
          !formData[`appoint-text-diff`].afterPointFileOnly.length
        ) {
          const msgType = !formData[`appoint-text-diff`].beforePointFileOnly.length
            ? '范文'
            : '比对'
          return { msg: `未上传特定页比对中的${msgType}文件,请确认` }
        }
      }
      break

    default:
      break
  }
}

//校验未通过时,会返回一个object,否则返回undefind,因而:

(//校验的方式多种多样,只需让用户感知有误即可,我们甚至可以模仿表单项下手动加一个警示语句如

)


const confirm = () => {
  const checkError = valiteForm()
  if (checkError) {
    
    return MessagePlugin.warning(checkError.msg)
  }
}

原本写到这里,只需将formdata数据返回给父组件然后再调用重置方法即可,然而还是有坑:

reactive异步问题

查看以下代码:

const confirm = () => {
 const checkError = valiteForm()
 if (checkError) {
   return MessagePlugin.warning(checkError.msg)
 }
 emit('success', formData)
 emit('update:visible', false)
 resetFormData()
}

逻辑是发送父组件当前数据,随后关闭抽屉,随后重置表单,但是这样写是有误的,表现是发给父级的是空数据,这是由于重置方法是同步的,而emit则是异步的,原以为只会出现在reacthook中的问题而今出现在了vue中,因而又有了以下解决方案:

// 实测此方式可大幅减少包体积
import cloneDeep from 'lodash/cloneDeep'
const confirm = () => {
  const checkError = valiteForm()
  if (checkError) {
    return MessagePlugin.warning(checkError.msg)
  }
// 预先拷贝一份,同步
  const formDataCopy = cloneDeep(formData)
  //此时formDataCopy为用户填入数据
  emit('success', formDataCopy)
  emit('update:visible', false)
  resetFormData()
}

至此坑就填完了,不过reactive在官方文档中已明确提示不建议继续使用,不明白的是为什么不可以使用ref.value[property]方式赋值,埋坑

以上。

posted @ 2024-06-30 16:10  致爱丽丝  阅读(18)  评论(0编辑  收藏  举报