1-Vue组件化实战【进阶】(2)
通用表单组件 收集数据、校验数据并提交。
实现以下组件
组件设计
原则是高内聚,低耦合; 组件的功能高度专一,这样可以使得组件使用变得灵活,组件之间的耦合度也会随之减小,从而可以提高组件的复用性。
Input.vue 组件
对着elmentui 简单的实现了一些功能,此段代码有一个问题是 this.$parent.$emit('validate') 触发校验是,this.$parent 会增加耦合度。
class="input-box"> <input :value="value" @input="handleInput" v-bind="$attrs" class="minput" :class="[ inputSize() ? 'm-input--' + inputSize() : '' ]"/> if="clearable" @click="clear">x
FormItem.vue 组件
class="form-item" prop:="name"> if="isErr" class="errInfo">{{ isErr }}
Form.vue 组件
为了让所有子组件都能访问到自身属性这里 inject了自己。
这里有个问题是 校验所有规则的时候 使用了 this.$children, 会增加组件之间的耦合度。
View.vue 组件
这里 this.$alert 弹窗组件下一小节再说
<Form :model="userInfo" :rules="rules" ref="loginForm">"用户名:" prop="name" ref="formItem"> <Minput v-model="userInfo.name" type="textarea" name="hong" size="big" clearable maxlength="10" placeholder="请输入帐号"/> "密码:" prop="pwd"> <Minput v-model="userInfo.pwd" type="password" maxlength="10" placeholder="请输入密码"/>
下面解决上面提到的两个耦合度的问题
在 elmentui 源码中发现它有更好的解决方法, 使用了 emitter.js 以混入(mixins)的方式
eimtter.js
里面有两个方法 一个 dispath 向上遍历父级组件,由于父级组件是单链的形式向上遍历,所以这种形式性能消耗比较小。
此方法主要是传入了组件名,和事件名,当遍历到父组件有此组件名则会,保存此父组件,让该父组件向自身派发事件
parent.$emit.apply(parent, [eventName, ...params])
/** * @description 向上遍历 父组件 并派发事件 * @param { String } componentName 组件名 * @param { String } eventName 派发事件名 * @param { Array } params 参数 */ dispath(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, ...params]) },
第二个方法是广播 broadcast
该方法是向下以树的形式遍历,消耗性能较大。实现事件派发的形式和 dispath 差不多,都是自己给自己派发事件
// 注意: 遍历组件,会有性能消耗 // 广播 function broadcast(componentName, eventName, params) { 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])); // 递归 } }) } export default { methods: { /** * @description 向上遍历 父组件 并派发事件 * @param { String } componentName 组件名 * @param { String } eventName 派发事件名 * @param { Array } params 参数 */ dispath(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, ...params]) }, // 向下派发 broadcast(componentName, eventName, params) { broadcast.call(this, componentName, eventName, params) } } }
解决 Input 的耦合
dispah 遍历父组件
解决 form 的耦合
由于广播会使得性能消耗较大,这里就不使用这种方法了。
当点击提交form 表单事件时,form 表单会校验所有需要校验的 form-item,需要校验的 form-item 会传入 prop 校验名称,因此之前使用 this.$children 的方法获取所有的 form-item 是否有 prop。
现在为了解耦,改变了思路,当 form-item 注册的时候向调用 dispath 派发事件,其实是父级给自己传事件,并传入自身(子组件)
form-item.vue
from.vue
至此组件之间的解耦成功