lib/SlotEvents.ts · Nick Zhang/vue3-slot-event - 码云 - 开源中国 (gitee.com)
用法
// 子组件中
<slot-events @click="handleClick">
<slot name="referer"/>
</slot-events>
// 父组件中
<component>test</component>
SlotEvents.ts
import {cloneVNode, Comment, defineComponent, Fragment, h, Text, VNode} from "vue";
/**
* 对于文本或SVG,需要用span包裹一下
* @param s
*/
const wrapTextContent = (s: string | VNode): VNode => {
return h('span', null, s);
};
/**
* 判断是否为Object
* @param val
*/
const isObject = (val: any) => val !== null && typeof val === 'object';
/**
* 找出第一个合法的子元素
* @param node
*/
const findFirstLegitChild = (node: VNode[] | undefined): VNode | null => {
if (!node) return null;
for (const child of node) {
if (isObject(child)) {
switch (child.type) {
case Comment:
continue;
case Text:
case 'svg':
return wrapTextContent(child);
case Fragment:
return findFirstLegitChild(child.children as VNode[]);
default:
return child;
}
}
return wrapTextContent(child);
}
return null;
};
const NAME = 'SlotEvents';
const SlotEvents = defineComponent({
name: NAME,
setup(_, {slots, attrs}) {
return () => {
const defaultSlot = slots.default?.(attrs);
if (!defaultSlot) return null;
if (defaultSlot.length > 1) {
console.warn(`${NAME}: 只需要一个子元素`);
return null;
}
const firstLegitChild = findFirstLegitChild(defaultSlot);
if (!firstLegitChild) {
console.warn(`${NAME}: 没有可用的子元素`);
return null;
}
return cloneVNode(firstLegitChild, attrs);
};
}
});
export default SlotEvents;