最近在项目中二次封装了Modal弹窗,目的是为了项目中的弹窗统一,样式修改方便,多人开发的时候不会出现各种不同的代码,封装的时候也踩了坑,下面总结一下封装经验:
//JSX代码
import { defineComponent, Fragment } from "vue"
import { Spin, Modal } from 'ant-design-vue'
export default defineComponent({
name: 'btModal',
props:{
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: ''
},
width: {
type: Number
},
//是否展示关闭按钮
closable: {
type: Boolean,
default: false
},
// 点击遮罩是否可关闭
maskClosable: {
type: Boolean,
default: false
},
// 不展示 footer
footer: {
type: String,
default: null
},
bodyStyle: Object, // Modal body 样式
// 关闭时是否销毁 Modal 里的子元素
destroyOnClose: {
type: Boolean,
default: true
},
// 是否支持键盘 esc 关闭
keyboard: {
type: Boolean,
default: false
},
// 遮罩 modal 的loading 样式
loading: {
type: Boolean,
default: false
},
},
setup(props, { slots, attrs }) {
const modalSlots = Object.keys(slots).map(slot => {
return (
<Fragment v-slots={slot}>
{slots[slot]()}
</Fragment>
)
})
const btModal = () => {
const {loading} = props;
return (
<Spin
spinning={loading}
>
<Modal {...attrs} {...props}>
{modalSlots}
</Modal>
</Spin>
)
}
return () => (
<div style="position: relative">
{btModal()}
</div>
)
}
})
其中需要注意的是:
1、循环渲染slot方法:modalSlots中,必须调用一下 {slots[slot]()},因为是个函数,不调用,页面不会呈现slot内容。
2、在父组件中,我用的vue2.6中的写法,具名插槽用 # 为前缀命名,这里需要注意,如果是默认插槽,也需要给个标注,如果不给标注,那么有名字的插槽会被优先渲染出来,没有名字的插槽会最后渲染,这样就不符合你需要的页面样式,所以默认的插槽需要用 #default 来标注一下。具名插槽就正常起名字就好,比如 #footer。
有默认名字的插槽代码:
<ModalInfo :visible="state.modalVisible" :title="'modal弹窗title'">
<template #default><span>这是标注了default的插槽内容.</span></template>
<template #footer>
<div class="modal-footer">
<Button @click="closeFunc">Return</Button>
<Button type="primary" @click="closeFunc">Submit</Button>
</div>
</template>
</ModalInfo>
结果:
再看没有默认标注的插槽代码:
<ModalInfo :visible="state.modalVisible" :title="'modal弹窗title'">
<span>这是没有任何标注的内容</span>
<template #footer>
<Button>Return</Button>
<Button type="primary">Submit</Button>
</template>
</ModalInfo>
再看结果:文字被渲染到了按钮的下方:
总结:
如果是默认插槽,必须给标注,如果不给标注,那么有名字的插槽会被优先渲染出来,没有名字的话会最后渲染。