函数式组件
利用调用函数的方式创建组件,该组件可以满足于消息提醒,模态框渲染。一般在市场上通用的ui组件库中比较常见于messageBox(消息提示组件)。
为什么要自己实现函数式组件呢
作者在使用第三方库 antv/g6 时出现在手机端无右键菜单选项时突发奇想想到自己做一个能兼顾库又能在自定义Behavior的中顺利运用所想到的。
代码实现
1、contextmenuBox.vue文件
<template>
<div class="fnCpn"
:style="{
left: props.x + 'px',
top: props.y + 'px'
}"
>
<div class="item"
v-for="item in props.list"
:key="item.value"
:class="[
item.value === props.nowVal? 'active' : ''
]"
@click="handleItemClick(item)"
>
{{item.label}}
</div>
</div>
</template>
<script setup lang="ts">
type ListItem = {label: string, value: string};
interface PropType {
// 菜单选项
list: Array<ListItem>,
// 目前所选的目标值
nowVal: string,
// 右键鼠标的x值
x: number,
// 右键鼠标的y值
y: number,
// 关闭函数
closeFn: <T = any>(value: ListItem) => void
}
const props = withDefaults(defineProps<PropType>(), {
nowVal: ''
})
// 点击菜单处理函数
// 可拓展其他逻辑
const handleItemClick = (value: ListItem) => {
props.closeFn(value);
}
</script>
<style scoped lang="scss">
.fnCpn{
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
//width: 80px;
background: white;
position: fixed;
.item{
width: 80px;
border: 1px solid #eee;
text-align: center;
font-size: 14px;
padding: 8px;
cursor: pointer;
}
.active{
background: #E6A23C;
color: white;
}
}
</style>
2、renderCpn.ts
import {createApp} from "vue";
import messageBox from "./contextmenuBox.vue";
// 函数式创建组件函数
export default function createContextmenuBox(
// 菜单列表
list: Array<{label: string, value: string}>,
// 当前所选值
nowVal: string,
// 要渲染的位置
position: {x: number, y: number},
// 是否关闭的回到函数
callback: <T = any>(value: {label: string, value: string}) => boolean | Promise<T>,
// 渲染到根DOM
root: HTMLElement = document.body
) {
// 删除dom的函数,当传递的callback返回的值为false或者Promise.reject()时都不会销毁当前dom
const close = async <T = any>(value: {label: string, value: string}) => {
try {
const result = await callback(value);
if(typeof result === 'boolean' && !result) return;
// 卸载组件
messageComponent.unmount();
} catch (e) {
throw new Error(e);
}
}
// 函数式组件生成
const messageComponent = createApp(messageBox, {
list,
nowVal,
x: position.x,
y: position.y,
closeFn: close
});
// 挂载到相应的dom上
messageComponent.mount(root);
}
3、在组件的中的使用(这里贴的代码于g6无关,只是简单的通用场景)
<template>
<div class="clickArea"
style="width: 100vw;height: 1000px;background: #747bff"
@contextmenu.prevent="handleContextmenu($event)"
ref="contentRef"
>
</div>
</template>
<script lang="ts" setup>
import createContextmenuBox from "@/components/functionComponent/renderCpn.ts";
import {ref} from "vue"
const list = [
{label: '复制', value: 'copy'},
{label: '粘贴', value: 'stick'},
{label: '删除', value: 'delete'}
]
// 右键菜单事件
const handleMenuClick = (value: {label: string, value: string}) => {
console.log("传递的值接受到了:", value)
return Promise.reject("特意设计的error");
// return false
}
const contentRef = ref()
// 右键菜单处理事件
const handleContextmenu = (e: MouseEvent) => {
const {offsetX, offsetY} = e;
createContextmenuBox(
list,
'delete',
{x: offsetX, y: offsetY},
handleMenuClick,
contentRef.value
)
}
</script>
4、代码结构图