1-Vue组件化实战【进阶】(2)


通用表单组件 收集数据、校验数据并提交。

 实现以下组件

 组件设计

原则是高内聚,低耦合; 组件的功能高度专一,这样可以使得组件使用变得灵活,组件之间的耦合度也会随之减小,从而可以提高组件的复用性。

 Input.vue 组件

对着elmentui 简单的实现了一些功能,此段代码有一个问题是  this.$parent.$emit('validate') 触发校验是,this.$parent 会增加耦合度。





  FormItem.vue 组件





Form.vue 组件

为了让所有子组件都能访问到自身属性这里 inject了自己。

这里有个问题是 校验所有规则的时候 使用了 this.$children, 会增加组件之间的耦合度。



View.vue 组件 

这里 this.$alert 弹窗组件下一小节再说





下面解决上面提到的两个耦合度的问题

在 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

 至此组件之间的解耦成功

vue