项目场景
项目中通过表单来填写校验大量复杂数据
问题描述
项目中使用的是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
2.简单使用
导入
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)
},
3.记得发布了消息 要在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>
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。