Vue 自定义组件开发心得-对话框

目录

组件的封装我认为要遵循的原则

Dialog显隐在组件外维护

Dialog 显隐在组件内维护

同时使用多个Dialog时的代码风格对比

el-dialog写在父组件

el-dialog写在子组件

小结


组件的封装我认为要遵循的原则

  • 不要使用过多的prop
  • 不要轻易更新v-model的值
  • watch方法中的内容尽量抽离到具体click或者callback业务上,否则代码多的情况不容易定位是哪里触发了watch方法
  • 减少使用watch prop——监听外部传递的prop改变而触发内部代码更新的操作
  • 组件抽离尽量精简,业务抽离得彻底,避免父组件反复调用子组件的方法

开发过程中发现一个比较有争议的地方

项目中使用element-ui,经常用到el-dialog组件

那么如果要把弹出对话框的内容封装为一个组件的话,有两种情况

  1. el-dialog 写在组件外,由父组件控制显示隐藏
  2. el-dialog 写在组件内部,由组件自己维护对话框显示隐藏

本次,就这两种差异发表个人浅见,讨论那种方式更好

Dialog显隐在组件外维护

<!-- 父组件 -->
<template>
    <div>
        <el-dialog :visible.sync="showDialog"> <!-- el-dialog写在组件外 -->
            <my-component ref="myComponent" @close="showDialog = false"/>
        </el-dialog>
    </div>
</template>
<script>
import myComponent from '@/myComponent';
export default {
    component: { myComponent }
    data(){
        return {
            showDialog: false
        }
    }
}
</script>

<!-- 子组件 -->
<template>
    <div>
        <button @click="handleClose">close</button>
    </div>
</template>
<script>
export default {
    methods: {
        handleClick(){
            this.$emit('close'); // 子组件点击关闭按钮,通知父组件关闭弹框
        }
    }
}
</script>

父组件定义data变量,控制dialog对话框的显隐。而对话框的点击的取消事件也需要通过@方法通知父组件更新显隐状态。

当然,也可以使用v-model 或者:prop.sync的方式,使子组件也可以有改变父组件变量的能力,这样也可以,不过底层实现还是通过@的方式,由子组件通知父组件更新变量,看起来像是数据的反向流动。一般还是建议数据单向流动,减少子组件对父组件变量的修改,尤其是复杂组件。

好处:

而这种方式的好处在于,开发父组件时,可以在使用时决定子组件是否需要弹框,需要弹框就套上弹框代码。适用于偶尔要把这个组件做成弹框的情况。

问题:

对话框状态由父组件控制的情况,少量的话还好,多的时候父组件就难免在data中定义更多“show”的变量,这些变量也会在代码的不同角落被修改,增加了代码耦合度。

Dialog 显隐在组件内维护

<!--父组件-->
<template>
    <div>
       <my-component ref="myComponent" @confirm="(val) => {}"/> <!-- 确定事件将结果通过@传递出来 -->
    </div>
</template>
<script>
import myComponent from '@/myComponent';
export default {
    component: { myComponent }
    data() {},
    mounted(){
        this.$refs.myComponent.show(); // 通过调用子组件方法这打开对话框,这个方法可以传递一些初始参数,减少prop的使用
        this.$refs.myComponent.showDialog = true; // 也可以直接更改子组件showDialog的值(不推荐)
    }
}
</script>


<!--子组件-->
<template>
    <el-dialog :visible.sync="showDialog" @open="handleOpen"> <!-- 子组件维护对话框显隐 -->
        <div>hello world ...</div>
        <button @click="showDialog = false">Close</button>
        <button @click="$emit('confirm','123123')">Confirm</button>
    </el-dialog>
</template>
<script>
 export default {
    data() {
        return {
            showDialog: false
        }
    },
    methods: {
        show(){
            this.showDialog = true;
            this.$nextTick(()=> {
                // this.$refs.table.query() // el-dialog懒渲染,需要使用nextTick初始化数据
            });
        },
        handleOpen(){ // 由于el-dialog懒渲染,或使用官方提供的open回调
            //this.$refs.table.query() // 这样,第一次打开就能获取到this.$refs.的内容了
        }
    }
}
</script>

个人偏向于写在组件内部,原因是作为对话框弹出时,用户一般是操作不到父组件的内容的,实际上父组件一般只需要一个“打开”对话框的操作,子组件将对话框中的结果传回父组件,关闭对话框的业务逻辑也一般在对话框内进行。

父组件通过调用子组件的show()方法这打开对话框,这个方法也可以传递一些初始参数,以减少prop的使用。

因为我发现在打开对话框时,向组件传递的部分参数,自打开对话框后,父组件一般就不更新这些参数了,不一定要使用prop来传递。

优点:

在抽取这部分参数放到(比如show)方法中传入后,父组件也往往会减少一些data参数的定义(控制show的变量组件初始化的参数),让父组件业务更加清晰。

问题:

  1. 使用组件时,显示对话框的入口函数规范问题,而且由于对话框写在组件内部,使用时不可去掉对话框。
  2. el-dialog懒渲染问题,第一次打开时才开始渲染slot中的内容,this.$refs会找不到,解决:1.使用this.$nextTick   2.使用官网的,在el-dialog中@open/@opened回调中操作DOM

同时使用多个Dialog时的代码风格对比

el-dialog写在父组件

<!-- 对话框状态在父组件维护-->
<template>
    <div>
        <el-dialog :show.aync="show.comp1">
            <my-component1
                :param="param1"
                @close="handleClose1" 
                @confirm="handleConfirm1"
            />
        </el-dialog>

         <el-dialog :show.aync="show.comp2">
            <my-component2
                :param="param2"
                @close="handleClose2"
                @confirm="handleConfirm2"
            />
        </el-dialog>

         <el-dialog :show.aync="show.comp1">
            <my-component3
                :param="param3"
                @close="handleClose3"
                @confirm="handleConfirm3"
            />
        </el-dialog>
    </div>
</template>
<script>
import myComponent1 from '@/myComponent1';
import myComponent2 from '@/myComponent2';
import myComponent3 from '@/myComponent3';
export default {
    component: { myComponent1,myComponent2,myComponent3 }
    data() {
        return {
            show: {
                comp1: false,
                comp2: false,
                comp3: false,
            },
            param1: {
                key: 1,
                val: 2
            },
            param2: {
            },  
            param3: {
            },          
        }
    },
    mounted(){
        this.show.comp1 = true;
    },
    methods: {
        handleClose1(){
            this.show.comp1 = false;
        },
        handleClose2(){
            this.show.comp2 = false;
        },
        handleClose3(){
            this.show.comp3 = false;
        },
        handleConfirm1(){
            this.show.comp1 = false;
            this.show.comp2 = true;
            this.param2 = {
                key: 'key2',
                val: 'val2',
            }
        }
        handleConfirm2(){
            this.show.comp2 = false;
            this.show.comp3 = true;
            this.param3 = {
                key: 'key3',
                val: 'val3',
            }
        
        }
        handleConfirm3(){
            this.show.comp3 = false;
            console.log('确认3');
        }
    }
}
</script>

父组件代码量显著增多,尤其是有关控制显隐的代码。 

el-dialog写在子组件

<!-- 对话框状态在子组件维护 -->
<template>
    <div>
       <my-component1 ref="myComponent1" @confirm="handleConfirm1"/>

       <my-component2 ref="myComponent2" @confirm="handleConfirm2"/>

       <my-component3 ref="myComponent3" @confirm="handleConfirm3"/>
    </div>
</template>
<script>
import myComponent1 from '@/myComponent1';
import myComponent2 from '@/myComponent2';
import myComponent3 from '@/myComponent3';
export default {
    component: { myComponent1,myComponent2,myComponent3 }
    data() {
        return {
        }
    },
    mounted(){
        this.$refs.myComponent1.show(); // 显示对话框1
    },
    methods: {
        // 处理组件1确认事件
        handleConfirm1(){
            let param = { // 也可以传递参数到子组件
                key: 'key2',
                val: 'val2'
            }
            this.$refs.myComponent2.show(param); // 显示对话框2
        },
        // 处理组件2确认事件
        hanldeConfirm2(){
            let param = { // 也可以传递参数到子组件
                key: 'key3',
                val: 'val3'
            }
            this.$refs.myComponent3.show(param); // 显示对话框3
        },
        // 处理组件3确认事件
        hanldeConfirm3(){
            console.log('组件3确认');
        }
    }
}
</script>

组件内的变量组件内部维护,通过内部方法来控制内部变量的改变(show变量),有点借鉴于JAVA面向对象的编程思想。

这种风格也有点像jQuery了,更加直观。

可复用确认组件


<!--父组件-->
<template>
    <div>
       <my-component ref="myComponent" />
    </div>
</template>
<script>
import myComponent from '@/myComponent';
export default {
    component: { myComponent },
    mounted(){
      // 通过调用子组件方法这打开对话框,这个方法可以传递一些初始参数,减少prop的使用
        this.$refs.myComponent.show().then(() => {
          // 点击确定回调
        }).catch(() => {
          // 点击取消回调
        }); 
       
    }
}
</script>
<!--子组件-->
<template>
    <el-dialog :visible.sync="showDialog"> <!-- 子组件维护对话框显隐 -->
        <div>hello world ...</div>
        <button @click="onClose">Close</button>
        <button @click="onConfirm">Confirm</button>
    </el-dialog>
</template>
<script>
 export default {
    data() {
        return {
            showDialog: false,
            resolve: null,
            reject: null
        }
    },
    methods: {
        show(){
            this.showDialog = true;
            return Promise((resolve,reject) => {
              this.resolve =resolve
              this.reject =reject
            })
        },
        onConfirm(){
          this.showDialog = false
          this.resolve()
        },
        onClose(){
          this.showDialog = false
          this.reject()
        }
       
    }
}
</script>

通过show方法抛出Promise对象,并保存期resolve,reject方法,并在对话框中的确认,取消事件中执行。这样在父元素就可以通过then链判断对话框是否确认和关闭

封装包

类似ui库一样,提供install方法。这样式样上就类似ui库一样,Vue.use的方式来使用。

import Dialog from 'xxx.vue';
export default function(Vue){
    Vue.component(Dialog.name, Dialog);
}

 为了便于按需加载组件,也需要将原vue组件代码导出。类似 下面两种方式。

方式1

export { default as Dialog } from 'xxx.vue';

方式2


import Dialog from 'xxx.vue';

export default {
    component: Dialog,
    install(Vue){
        Vue.component(Dialog.name,Dialog);
    }
}

 这样就可以直接获取到组件了。

小结

显然对话框写在子组件内部具有更好的可读性和更低的代码耦合度,使父组件的开发更聚焦于业务逻辑。这也使对话框与子组件业务绑定,无法选择去除对话框。子组件内容初始化需要考虑el-dialog的懒渲染。

对话框由父组件控制则在开发过程中更方便选择是否使用对话框,代价是更高的代码耦合。

以上是一点关于vue对话框的一些小总结,还未完全钻研透彻。比如使用:prop.sync传递参数,控制对话框显示与否,这样也可以在一定程度上减少父组件代码量。

若有错误,敬请指出更正。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值