项目场景:

项目中通过表单来填写校验大量复杂数据


问题描述:

项目中使用的是Ant Design of Vue这个组件库,使用FormModel 表单,数据字段和校验较多时,表单操作卡顿;eg: a-input输入框,等你输入完字及,几秒后才慢慢出现你输入的字符

原因分析:

vue在进行输入时,进行了多次的render刷新渲染操作,导致了input框输入时发生的卡顿现象


解决方案:

官方给出的解决办法,将 Form 相关的业务独立到一个单独的组件中,减少组件渲染的消耗,如果有很多校验项,可把它们分别放在不同的Form中处理

eg :

一、 组件封装

在这里插入图片描述
将大表单拆分成三个组件表单,数据校验等操作在其组件内部实现

二、 formTable中的字段有部分与formTableTwo联动,这里使用PubSub.js进行兄弟组件传值

  • PubSub.js的使用
  1. 首先安装pubsub-js
	npm install --save pubsub-js
  1. 简单使用
    导入
	import PubSub from 'pubsub-js'

发送消息:PubSub.publish(名称,参数)
订阅消息:PubSub.subscrib(名称,函数)
取消订阅:PubSub.unsubscrib(名称)

在formTableTwo中使用PubSub.publish(名称,参数)发送信息,formTableOne中使用PubSub.subscrib(名称,函数)接收信息。
注意

  1. PubSub.subscrib(名称,函数)接收信息的所传名称要与PubSub.publish(名称,参数)发送信息的名称一致
  2. PubSub.subscrib(名称,函数)接收信息可能会被触发多次,可以在PubSub.subscrib(名称,函数)前使用 PubSub.unsubscribe可以解决
  created () {
    // console.log('form1', this.form)
    // 解决PubSub多次调用
    PubSub.unsubscribe('send');
    // 订阅消息(接收消息)
    PubSub.subscribe('send', (name, value) => {
      console.log('name', name)
      console.log('value', value)
    })
    // 订阅组件二的消息
    PubSub.subscribe('sendTwo', (name, val) => {
      console.log('sendTwo', name)
      console.log('我是接受到的值', val)
      this.isShow = val
    })
    console.log('this.form', this.form)
  },
  1. 记得发布了消息 要在vue beforedestory 中销毁取消订阅 ,发布的次数多了,会造成订阅一次触发多次的情况;
  beforeDestroy () {
    PubSub.unsubscribe('send')
    PubSub.unsubscribe('sendTwo')
  }

三、使用Promise.all提交校验表单

  • 子组件
    onSubmit () {
      return new Promise((resolve, reject) => {
        this.$refs.ruleForm.validate(valid => {
          if (valid) {
            console.log('表单1通过')
            this.outgoingInfo()
            resolve(valid)
          } else {
            console.log('error submit!!')
            reject(valid)
            return false
          }
        })
      })
    },
  • 父组件
    // 表单校验
    submitForm () {
      console.log('this.$refs.FormTableOne.form', this.$refs.FormTableOne.form)
      const rules1 = this.$refs.FormTableOne.onSubmit()
      const rules2 = this.$refs.FormTableTwo.onSubmit()
      const rules3 = this.$refs.FormTableThree.onSubmit()
      Promise.all([rules1,rules2, rules3]).then(() => {
        console.log('校验通过')
      })
    },
  • 完整demo
  • formGroup
<template>
  <div>
    <div>
      <FormTableOne ref="FormTableOne" :formInfo="form" @outgoingInfo="outgoingInfo"/>
      <FormTableTwo ref="FormTableTwo" />
      <FormTableThree ref="FormTableThree" />
    </div>
      <a-button type="primary" @click="submitForm">
        submit
      </a-button>
      <a-button style="margin-left: 10px;" @click="resetForm">
        Reset
      </a-button>
  </div>
</template>

<script>
import FormTableOne from './components/formTableOne'
import FormTableTwo from './components/formTableTwo'
import FormTableThree from './components/formTableThree'
import PubSub from 'pubsub-js'
export default {
  components: {
    FormTableOne,
    FormTableTwo,
    FormTableThree
  },
  data () {
    return {
      form: {}
    }
  },
  created() {
    this.sendMessages()
    setTimeout(() => {
      this.form = {
         name1: '123456',
          region1: undefined,
          date1: undefined,
          delivery1: false,
          type1: [],
          resource1: '123',
          desc1: '123',
          name2: '',
          region2: undefined,
          date2: undefined,
          delivery2: false,
          type2: [],
          resource2: '',
          desc2: '',
          name3: '',
          region3: undefined,
          date3: undefined,
          delivery3: false,
          type3: [],
          resource3: '',
          desc3: ''
      }
    }, 2000)
  },
  methods: {
    // 表单校验
    submitForm () {
      console.log('this.$refs.FormTableOne.form', this.$refs.FormTableOne.form)
      const rules1 = this.$refs.FormTableOne.onSubmit()
      const rules2 = this.$refs.FormTableTwo.onSubmit()
      const rules3 = this.$refs.FormTableThree.onSubmit()
      Promise.all([rules1,rules2, rules3]).then(() => {
        console.log('校验通过')
      })
    },
    outgoingInfo (val) {
      console.log('组件一传出的值', val)
      Object.assign(this.form, val)
      console.log('主组件的值', this.form)
    },

    resetForm () {
      this.sendMessages()
      this.form = {
         name1: '',
          region1: undefined,
          date1: undefined,
          delivery1: false,
          type1: [],
          resource1: '',
          desc1: '',
          name2: '',
          region2: undefined,
          date2: undefined,
          delivery2: false,
          type2: [],
          resource2: '',
          desc2: '',
          name3: '',
          region3: undefined,
          date3: undefined,
          delivery3: false,
          type3: [],
          resource3: '',
          desc3: ''
      }
      console.log('this.form', this.form)
    },
    sendMessages () {
      console.log('发送')
      PubSub.publish('send', {
        name: '张三',
        age: 18
      })
    }
  }
}
</script>

<style></style>

  • formTableOne
<template>
  <div>
    <a-form-model
      ref="ruleForm"
      :model="form"
      :rules="rules"
      :label-col="labelCol"
      :wrapper-col="wrapperCol"
    >
      <a-form-model-item ref="name1" label="组件一: name" prop="name1" v-if="isShow">
        <a-input
          v-model="form.name1"
          @blur="
            () => {
              $refs.name1.onFieldBlur()
            }
          "
        />
      </a-form-model-item>
      <a-form-model-item label="组件一: zone" prop="region1">
        <a-select v-model="form.region1" placeholder="please select your zone">
          <a-select-option value="shanghai">
            Zone one
          </a-select-option>
          <a-select-option value="beijing">
            Zone two
          </a-select-option>
        </a-select>
      </a-form-model-item>
      <a-form-model-item label="组件一: time" required prop="date1">
        <a-date-picker
          v-model="form.date1"
          show-time
          type="date"
          placeholder="Pick a date"
          style="width: 100%;"
        />
      </a-form-model-item>
      <a-form-model-item label="组件一: delivery1" prop="delivery1">
        <a-switch v-model="form.delivery1" />
      </a-form-model-item>
      <a-form-model-item label="组件一: type1" prop="type1">
        <a-checkbox-group v-model="form.type1">
          <a-checkbox value="1" name="type1">
            Online
          </a-checkbox>
          <a-checkbox value="2" name="type1">
            Promotion
          </a-checkbox>
          <a-checkbox value="3" name="type1">
            Offline
          </a-checkbox>
        </a-checkbox-group>
      </a-form-model-item>
      <a-form-model-item label="组件一:Resource1s" prop="resource1">
        <a-radio-group v-model="form.resource1">
          <a-radio value="1">
            Sponsor
          </a-radio>
          <a-radio value="2">
            Venue
          </a-radio>
        </a-radio-group>
      </a-form-model-item>
      <a-form-model-item label="组件一: form" prop="desc1">
        <a-input v-model="form.desc1" type="textarea" />
      </a-form-model-item>
      <a-form-model-item :wrapper-col="{ span: 14, offset: 4 }">
      </a-form-model-item>
    </a-form-model>
    <a-button type="primary" @click="outgoingInfo">
      Create
    </a-button>
  </div>
</template>
<script>
import PubSub from 'pubsub-js'
const list = [
  '',
  'name1',
  'region1',
  'date1',
  'delivery1',
  'type1',
  'resource1',
  'desc1'
]
export default {
  name: 'FormTableOne',
  props: {
    formInfo: {
      type: Object,
      default: () => {
        return {
          name1: '',
          region1: undefined,
          date1: undefined,
          delivery1: false,
          type1: [],
          resource1: '',
          desc1: ''
        }
      }
    }
  },

  created () {
    // console.log('form1', this.form)
    // 解决PubSub多次调用
    PubSub.unsubscribe('send');
    // 订阅消息(接收消息)
    PubSub.subscribe('send', (name, value) => {
      console.log('name', name)
      console.log('value', value)
    })
    // 订阅组件二的消息
    PubSub.subscribe('sendTwo', (name, val) => {
      console.log('sendTwo', name)
      console.log('我是接受到的值', val)
      this.isShow = val
    })
    console.log('this.form', this.form)
  },

  data () {
    return {
      isShow: true,
      labelCol: { span: 4 },
      wrapperCol: { span: 14 },
      other: '',
      form: {
        name1: '',
        region1: undefined,
        date1: undefined,
        delivery1: false,
        type1: [],
        resource1: '',
        desc1: ''
      },
      rules: {
        region1: [
          {
            required: true,
            message: 'Please select Activity zone',
            trigger: 'change'
          }
        ],
        date1: [
          { required: true, message: 'Please pick a date', trigger: 'change' }
        ],
        resource1: [
          {
            required: true,
            message: 'Please select activity resource1',
            trigger: 'change'
          }
        ],
        desc1: [
          {
            required: true,
            message: 'Please input activity form',
            trigger: 'blur'
          }
        ]
      }
    }
  },
  watch: {
    // 过滤一些不属于这个组件属性
    formInfo () {
        let obj = JSON.parse(JSON.stringify(this.formInfo, (key, value) => {
          if (list.includes(key)) {
            return value
          } else {
            return undefined
          }
        }))
        this.form = Object.assign(this.form, obj)
    },
    deep: true
  },
  computed: {
  },

  methods: {
    onSubmit () {
      return new Promise((resolve, reject) => {
        this.$refs.ruleForm.validate(valid => {
          if (valid) {
            console.log('表单1通过')
            this.outgoingInfo()
            resolve(valid)
          } else {
            console.log('error submit!!')
            reject(valid)
            return false
          }
        })
      })
    },
    resetForm () {
      this.$refs.ruleForm.resetFields()
    },
    // 将组件的值传出去
    outgoingInfo () {
      this.$emit('outgoingInfo', this.form)
    }
  },
  beforeDestroy () {
    PubSub.unsubscribe('send')
    PubSub.unsubscribe('sendTwo')
  }
}
</script>

  • formTabeTwo
<template>
  <a-form-model
    ref="ruleForm"
    :model="form"
    :rules="rules"
    :label-col="labelCol"
    :wrapper-col="wrapperCol"
  >
    <a-form-model-item ref="name2" label="组件二: name" prop="name2">
      <a-input
        v-model="form.name2"
        @blur="
          () => {
            $refs.name2.onFieldBlur()
          }
        "
      />
    </a-form-model-item>
    <a-form-model-item label="组件二: zone" prop="region2">
      <a-select v-model="form.region2" placeholder="please select your zone">
        <a-select-option value="shanghai">
          Zone one
        </a-select-option>
        <a-select-option value="beijing">
          Zone two
        </a-select-option>
      </a-select>
    </a-form-model-item>
    <a-form-model-item label="组件二: time" required prop="date2">
      <a-date-picker
        v-model="form.date2"
        show-time
        type="date"
        placeholder="Pick a date"
        style="width: 100%;"
      />
    </a-form-model-item>
    <a-form-model-item label="组件二: delivery2" prop="delivery2">
      <a-switch v-model="form.delivery2" @change="checkChange"/>
    </a-form-model-item>
    <a-form-model-item label="组件二: type2" prop="type2">
      <a-checkbox-group v-model="form.type">
        <a-checkbox value="1" name="type2">
          Online
        </a-checkbox>
        <a-checkbox value="2" name="type2">
          Promotion
        </a-checkbox>
        <a-checkbox value="3" name="type2">
          Offline
        </a-checkbox>
      </a-checkbox-group>
    </a-form-model-item>
    <a-form-model-item label="组件二:resource2s" prop="resource2">
      <a-radio-group v-model="form.resource2">
        <a-radio value="1">
          Sponsor
        </a-radio>
        <a-radio value="2">
          Venue
        </a-radio>
      </a-radio-group>
    </a-form-model-item>
    <a-form-model-item label="组件二: form" prop="desc2">
      <a-input v-model="form.desc2" type="textarea" />
    </a-form-model-item>
    <a-form-model-item :wrapper-col="{ span: 14, offset: 4 }">
    </a-form-model-item>
  </a-form-model>
</template>
<script>
import PubSub from 'pubsub-js'
export default {
  name: 'FormTableOne',
  data () {
    return {
      labelCol: { span: 4 },
      wrapperCol: { span: 14 },
      other: '',
      form: {
        name2: '',
        region2: undefined,
        date2: undefined,
        delivery2: false,
        type2: [],
        resource2: '',
        desc2: ''
      },
      rules: {
        region2: [
          {
            required: true,
            message: 'Please select Activity zone',
            trigger: 'change'
          }
        ],
        date2: [
          { required: true, message: 'Please pick a date', trigger: 'change' }
        ],
        resource2: [
          {
            required: true,
            message: 'Please select activity resource2',
            trigger: 'change'
          }
        ],
        desc2: [
          {
            required: true,
            message: 'Please input activity form',
            trigger: 'blur'
          }
        ]
      }
    }
  },
  created() {
    
  },
  methods: {
    onSubmit () {
      return new Promise((resolve, reject) => {
        this.$refs.ruleForm.validate(valid => {
          if (valid) {
            console.log('表单2通过')
            resolve(valid)
          } else {
            console.log('error submit!!')
            reject(valid)
            return false
          }
        })
      })
    },
    checkChange() {
      // 发布消息
       PubSub.publish('sendTwo', false)
    },
    resetForm () {
      this.$refs.ruleForm.resetFields()
    }
  }
}
</script>

  • formTableThree
<template>
  <a-form-model
    ref="ruleForm"
    :model="form"
    :rules="rules"
    :label-col="labelCol"
    :wrapper-col="wrapperCol"
  >
    <a-form-model-item ref="name3" label="组件三: name3" prop="name3">
      <a-input
        v-model="form.name3"
        @blur="
          () => {
            $refs.name3.onFieldBlur()
          }
        "
      />
    </a-form-model-item>
    <a-form-model-item label="组件三: zone" prop="region3">
      <a-select v-model="form.region3" placeholder="please select your zone">
        <a-select-option value="shanghai">
          Zone one
        </a-select-option>
        <a-select-option value="beijing">
          Zone two
        </a-select-option>
      </a-select>
    </a-form-model-item>
    <a-form-model-item label="组件三: time" required prop="date3">
      <a-date-picker
        v-model="form.date3"
        show-time
        type="date"
        placeholder="Pick a date"
        style="width: 100%;"
      />
    </a-form-model-item>
    <a-form-model-item label="组件三: delivery3" prop="delivery3">
      <a-switch v-model="form.delivery3" />
    </a-form-model-item>
    <a-form-model-item label="组件三: type3" prop="type3">
      <a-checkbox-group v-model="form.type">
        <a-checkbox value="1" name="type3">
          Online
        </a-checkbox>
        <a-checkbox value="2" name="type3">
          Promotion
        </a-checkbox>
        <a-checkbox value="3" name="type3">
          Offline
        </a-checkbox>
      </a-checkbox-group>
    </a-form-model-item>
    <a-form-model-item label="组件三:Resource3s" prop="resource3">
      <a-radio-group v-model="form.resource3">
        <a-radio value="1">
          Sponsor
        </a-radio>
        <a-radio value="2">
          Venue
        </a-radio>
      </a-radio-group>
    </a-form-model-item>
    <a-form-model-item label="组件三: form" prop="desc3">
      <a-input v-model="form.desc3" type="textarea" />
    </a-form-model-item>
    <a-form-model-item :wrapper-col="{ span: 14, offset: 4 }">
    </a-form-model-item>
  </a-form-model>
</template>
<script>
export default {
  name: 'FormTableOne',
  data () {
    return {
      labelCol: { span: 4 },
      wrapperCol: { span: 14 },
      other: '',
      form: {
        name3: '',
        region3: undefined,
        date3: undefined,
        delivery3: false,
        type3: [],
        resource3: '',
        desc3: ''
      },
      rules: {
        region3: [
          {
            required: true,
            message: 'Please select Activity zone',
            trigger: 'change'
          }
        ],
        date3: [
          { required: true, message: 'Please pick a date', trigger: 'change' }
        ],
        resource3: [
          {
            required: true,
            message: 'Please select activity resource3',
            trigger: 'change'
          }
        ],
        desc3: [
          {
            required: true,
            message: 'Please input activity form',
            trigger: 'blur'
          }
        ]
      }
    }
  },
  methods: {
    onSubmit () {
      return new Promise((resolve, reject) => {
        this.$refs.ruleForm.validate(valid => {
          if (valid) {
            console.log('表单3通过')
            resolve(valid)
          } else {
            console.log('error submit!!')
            reject(valid)
            return false
          }
        })
      })
    },
    resetForm () {
      this.$refs.ruleForm.resetFields()
    }
  }
}
</script>

Logo

前往低代码交流专区

更多推荐