巧妙利用混入解决$parent/$children带来的耦合性

巧妙利用混入解决$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的源码!
站内部分资源收集于网络,若侵犯了您的合法权益,请联系我们删除!
赞赏是最好的支持
如果对你有帮助那就支持一下吧
立即赞赏
分享到:
赞(2) 打赏

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

小月博客-一个专注于分享的技术博客
没有账号? 忘记密码?