巧妙利用混入解决$parent/$children带来的耦合性
最近在跟着开课吧学习web全栈的课程,说实话真的感觉自己底子太差了,什么都“新鲜”的不行! 所以只能下功夫去努力了,整理一下学习中学到的方法,总结有错误的地方希望大家提醒一下,共同进步吧。
一、 利用vue实现element组件表单校验(目标)
课程就是为了实现这样一个小案例,那么就涉及到了父子组件中的传值问题了,到底用什么是最合适的的呢?就成了我们今天讨论的重点了!大概功能效果如图所示:
页面效果图
二、如何解决局部校验和全局校验中的父子通信
方案1:利用$parent/$children获取需要当前表单校验的组件(校验用的async-validator插件)
Kinput组件
<input :type="type" :value="value" @input="onInput">
methods: {
onInput(e) {
this.$emit('input', e.target.value)
// 当数据发生变化,需要执行校验
// $parent 父组件只有一个插槽,所以让父组件去派发一个方法
this.$parent.$emit('validate')
}
},
KForm
provide(){
return{
form: this
}
},
props:{
model:{
type: Object,
required:true
},
rules:{
type: Object
}
},
methods: {
validate(cb) {
// 全局校验思路:
// 方案1: $children
// 1. 获取所有的Formitem
const tasks = this.$children
.filter(item => item.prop)
.map(item => item.validate());
}
},
//简单的组件结构 <template> <div> <KForm :model="model" :rules="rules" ref="form"> <KFormItem label="姓名" prop="username"> <KInput v-model="model.username"></KInput> </KFormItem> <KFormItem label="密码" prop="password"> <KInput v-model="model.password"></KInput> </KFormItem> <KFormItem > <button @click="login">登陆</button> </KFormItem> </KForm> </div> </template>
缺点: 以上方法实现这样的自定义组件结构是不会出错的,因为KForm组件下面只有KFormItem,没有其他的组件或者元素。但是如果出现了其他的子元素那么程序就会报错了,显然这样的方案对于用户的自定义是不适用的。
三、利用Vue混入来解决全局校验的功能
下面就是今天的重点了,我们要采用vue中非常好用的混入来解决这个问题
页面的组件dom不发生任何改变,我们可以去github上面查看element-ui源码是如何实现这样的功能的吧,
打开emitter.js文件里面是自定义的两个全局方法,
方法一“ 广播”
// broadcast 自上而下派发事件
// 参数1:组件名; 参数2: 事件名称; 参数3:参数数组
function broadcast(componentName, eventName, params) {
// 遍历所有的子元素,如果子元素componentName和传入的相同则派发事件
this.$children.forEach(child => {
var name = child.$options.componentName;
if (name === componentName) {
child.$emit.apply(child, [eventName].concat(params));
} else {
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
方法二 “冒泡查询”
// dispatch 自下而上,冒泡查找conponentName相同的组件并派发事件
dispatch(componentName, eventName, params) {
// 向上找父亲
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
// 向上查找直到找到相同名称的组件
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
// 如果找到就派发事件,同时跳出循环
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
在我们本地的src目录创建mixins/emitter.js文件,接下来我们来修改我们方案1代码的不健全性
修正input中$parent写法
1. mixins emitter
2. 声明componentName
3. dispath()
KInput.vue
import emitter from '@/mixins/emitter.js'
export default {
mixins: [emitter],
}
methods: {
onInput(e) {
this.$emit('input', e.target.value)
// 方案二: 冒泡查询并派发事件dispatch,我们的参数是动态的所以不用传递参数
// dispatch(componentName, eventName, params) {}
this.dispatch('KFormItem', 'validate')
}
},
KInputItem.vue
mounted () {
this.$on('validate', () =>{
this.validate()
})
// 派发事件通知KForm,新增一个KFromItem实例
if(this.prop) {
this.dispatch('KForm', 'kkb.form.addField', [this])
}
},
KForm.vue
created(){
// 添加,记得做移除的操作
this.fields = []
this.$on('kkb.form.addField', item =>{
this.fields.push(item)
})
},
methods: {
validate(cb) {
const tasks = this.fields.map(item => item.validate());
// 2. 执行他们的校验方法,如果大家的promise全部都resolve,校验通过
// 3. 如果其中有reject, catch() 中处理错误提示信息
Promise.all(tasks)
.then( () => cb(true))
.catch( () =>cb(false));
}
},
以上代码操作就可以实现父子之间传值的问题了。感觉总结的不太好,自己理解的不是很透彻,有时间了可以好好看看element-ui的源码!
























