表单提交规范
表单提交是用户主动触发的一种操作,用于搜集不同类型的用户输入。
在开发早期没有 ajax,主要依靠 form 表单通过编辑 action, method 属性通过页面跳转完成数据的收集
现代 form 表单更多的是指 JS 对象中的 form 对象,通过劫持其 onSubmit 事件进行数据校验和提交
注意事项
表单提交应是多方位触发的
表单提交都应由表单的 submit 事件触发,它可能是由 Button(type:submit)| input(type:submit)按钮,在表单直接按下回车键或是在移动端用软键盘按下提交键
因为提交维度众多所以我们应该将处理方法集中到 onSubmit 方法中,又因为表单设计之初是要页面跳转的所以我们要 preventDefault 阻止默认行为
Vue 中通过.prevent 修饰符可以直接阻止表单默认行为
<template>
<div>
<form @submit.prevent="handleSubmit">
<!-- 表单域 -->
</form>
</div>
</template>
表单可输入域的值应做 trim 操作
表单内用户可随意输入的表单域应在提交时做 空格 trim 处理
Vue 中通过 trim 修饰符可以很轻松的处理空格
<template>
<div>
<input v-model.trim="form.name" />
</div>
</template>
中间状态
表单内可以显示触发提交的按钮应加入中间状态控制重复提交
Vue
<template>
<button :disabled="submitting"></button>
</template>
防抖 Debounce
防抖的原理:你尽管触发事件,但是我一定在事件触发的 n 秒之后才执行,如果你在触发事件 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒之后在执行。
form表单提交加入防抖
Vue
//一种方法 在api层加防抖:
import { debounce } from 'lodash'
export const pressDebounce = debounce(function deleteReturnData(data) {
return request.post('/return/deleteCsReturnData/', {
data
})
}, 2000)
export default {
methods: {
async handleSubmit() {
if (!this.submitting) {
try {
await this.$refs.ruleForm.validate()
this.submitBtn()
} catch (e) {
console.error(e)
}
}
},
async submitBtn() {
try {
this.submitting = true
const res = await pressDebounce({ returnBatchId: 1 })
} catch (e) {
console.error(e)
} finally {
this.submitting = false
}
},
},
}
// 第二种在事件上加防抖
// 必须是事件名: 的写法 例如: handleSubmit: debounce
// 不能采用箭头函数形式()=>{},这样 this.$refs和this.form报错
// 原因是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.searchContractSuppList 将是 undefined 可参考https://cn.vuejs.org/v2/api#methods。
import { debounce } from 'lodash'
export default {
methods: {
handleSubmit: debounce(async function () {
if (!this.submitting) {
try {
await this.$refs.ruleForm.validate()
this.submitBtn()
} catch (e) {
console.error(e)
}
}
}, 1000),
async submitBtn() {
try {
this.submitting = true
const res = await pressDebounce({ returnBatchId: 1 })
} catch (e) {
console.error(e)
} finally {
this.submitting = false
}
},
}
}
React + Dva
import {delay} from 'dva/saga'
debouncedCallEffect: [
function* (action, { put }) {
yield delay(1000);
yield put({
...action,
type: 'callEffect',
});
}, { type: "takeLatest" }
// takeLatest 并不是纯粹意义的节流,只是响应最后一次结果
],
callEffect: [*function(action,{call}){
const response = yield call(api)
}, {type: 'takeLatest'}],
节流 throttle
节流的原理: 如果你持续触发事件,每隔一段时间,只会执行一次事件
提交内容过多的后端处理时间较长的应做节流处理
Vue
export default {
created() {
this.throttleHandleSubmit = lodash.throttle(this.handleSubmit, 5000),
},
methods: {
handleSubmit() {}
},
}
React + Dva
// dva modal // 并且给button组件加loading
// loading: loading.effects['nameSpace/callEffect'],
callEffect: [*function(action,{call}){
const response = yield call(api)
return response
}, {type: 'throttle', ms: 5000}]
校验
国产 UI 框架的 form 表单校验器大多基于async-validator,已经支持了众多校验模式和异步 Promise,熟读其文档很重要
// 自己实现简单校验方法
/**
* vue中自己实现简单表单校验
*/
import { message } from 'ant-design-vue';
export default {
install(Vue) {
Vue.prototype.validateForm = function(validateArray) {
console.log("validateArray=>",validateArray)
let formArray = validateArray || Object.keys(this.formData);
return !formArray.some((name) => {
let rules = this.rules[name],
inputValue = this.formData[name];
if (!rules) {
return false;
}
// 如果是必填字段
if (rules.required && !inputValue) {
message.error(rules.requiredMsg);
return true;
}
// 如果是需要判断
if (rules.validate) {
if (!rules.validate.test(inputValue)) {
message.error(rules.validateMsg);
return true;
}
}
return false;
});
};
}
};
// 使用
// main.js中
Vue.use(validateForm);
// 业务页面当中
export default {
name: "login",
data() {
return {
formData: {
userMobile: "",
userPassword: "",
},
rules: {
userMobile: {
required: true,
requiredMsg: "账号不能为空",
validate: /^1[3456789]\d{9}$/,
validateMsg: '手机号码格式错误'
},
userPassword: {
required: true,
requiredMsg: "密码不能为空",
},
},
};
},
methods: {
login() {
// 若单个使用可以传入参数this.validateForm(['userMobile']) 不传参数默认是校验全部
if (!this.validateForm()) {
return false;
}
}
}
}
ReactNative 中 表单校验一般都是自己写没有定制化需求,也可以使用以上方法
import Toast from './toast'
function validateForm (validateArray) {
let formArray = validateArray|| Object.keys(this.formData) ;
return !formArray.some( name => {
let rules = this.state.rules[name],
inputValue = this.state[name];
if(!rules){
return false;
}
// 如果是必填字段
if (rules.required && !inputValue){
Toast.show(rules.requiredMsg)
return true;
}
// 如果是需要判断
if (rules.validate){
if(!rules.validate.test(inputValue)){
Toast.show(rules.validateMsg)
return true;
}
}
return false;
});
};
// 使用
import validateForm from 'utils/validateForm'; //引入
this.state={
formData:{
domainName:""
},
domainName: {
required: true,
requiredMsg: '请输入主域名',
validate: /^1[3456789]\d{9}$/,
validateMsg: '手机号码格式错误'
},
}
// 验证 react native没有注入实例 此处需要使用call来改变this指向
if (!validateForm.call(this,['domainName'])) {
return false;
}
react native表单防抖节流都可使用lodash插件绑定方法实现