学会使用listeners,二次包装组件就靠它了
前几天产品经理给我甩过来一份管理系统的设计原型,我打开看了看,虽然内心是拒绝的,但是为了活着,还是要做的。小编看了看原型,发现系统中的大部分弹框右下角都是确定和取消两个按钮。如果使用element-ui提供的Dialog,那么每一个弹框都要手动加按钮,不但代码量增多,而且后面如果按钮UI,需求发生变化,改动量也比较大。
image.png如果可以将Dialog进行二次封装,将按钮封装到组件内部,就可以不用重复去写了。说干就干。
定义基本弹框代码
<template>
<el-dialog :visible.sync="visibleDialog">
<!--内容区域的默认插槽-->
<slot></slot>
<!--使用弹框的footer插槽添加按钮-->
<template #footer>
<!--对外继续暴露footer插槽,有个别弹框按钮需要自定义-->
<slot name="footer">
<!--将取消与确定按钮集成到内部-->
<span>
<el-button @click="$_handleCancel">取 消</el-button>
<el-button type="primary" @click="$_handleConfirm">
确 定
</el-button>
</span>
</slot>
</template>
</el-dialog>
</template>
<script>
export default {
props: {
// 对外暴露visible属性,用于显示隐藏弹框
visible: {
type: Boolean,
default: false
}
},
computed: {
// 通过计算属性,对.sync进行转换,外部也可以直接使用visible.sync
visibleDialog: {
get() {
return this.visible;
},
set(val) {
this.$emit("update:visible",val);
}
}
},
methods: {
// 对外抛出cancel事件
$_handleCancel() {
this.$emit("cancel");
},
// 对外抛出 confirm事件
$_handleConfirm() {
this.$emit("confirm");
}
}
};
</script>
通过上面的代码,我们已经将按钮封装到组件内部了,效果如下图所示:
<!--外部使用方式 confirm cancel 是自定义的事件 opened是包装el-dialog的事件,通过$listeners传入到el-dialog里面-->
<custom-dialog :visible.sync="visibleDialog" @opened="$_handleOpened" @confirm="$_handleConfirm" @cancel="$_handleCancel">这是一段内容</custom-dialog>
image.png
但上面的代码存在一个问题,无法将Dialog自身的属性和事件暴露到外部(虽然可以通过props及attrs与$listeners
使用listeners
attrs属性内(class与style除外,他们会挂载到组件最外层元素上)。并可通过v-bind="$attrs"传入到内部组件中
listeners获取到。并可通过v-on="$listeners"传入到内部组件中。
修改弹框代码
<!---使用了v-bind与v-on监听属性与事件-->
<template>
<el-dialog :visible.sync="visibleDialog" v-bind="$attrs" v-on="$listeners">
<!--其他代码不变-->
</el-dialog>
</template>
<script>
export default {
//默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings)
//将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。
//通过设置 inheritAttrs 到 false,这些默认行为将会被去掉
inheritAttrs: false
}
</script>
<!---外部使用方式-->
<custom-dialog
:visible.sync="visibleDialog"
title="测试弹框"
@opened="$_handleOpened"
>
这是一段内容
</custom-dialog>
对于props来代替,实现代码如下
<template>
<el-dialog :visible.sync="visibleDialog" v-bind="$props" v-on="$listeners">
<!--其他代码不变-->
</el-dialog>
</template>
<script>
import { Dialog } from 'element-ui'
export default {
props: {
// 将Dialog的props通过扩展运算符展开到props属性里面
...Dialog.props
}
}
</script>
但上面的代码存在一定的缺陷,有些组件存在非props的属性,比如对于一些封装的表单组件,我们可能需要给组件传入原生属性,但实际原生属性并没有在组件的props上面定义,这时候,如果通过上面的方式去包装组件,那么这些原生组件将无法传递到内部组件里面(感谢@陌上兮月的提醒)
网友评论