之前在
vue2
中实现过函数式调用
组件,那么在vue3
中该如何实现呢?
让我们在vue3
中来实现一个函数式调用
的组件吧
1. 在/src/components
下创建Toast
文件夹
在Toast
文件夹下创建toast.vue
<script setup lang="ts">
import { ref, unref } from "vue";
const _show = ref(false);
const _message = ref("hello");
const open = (message: string, delay = 1000) => {
if (_show.value) return;
_message.value = message;
_show.value = true;
setTimeout(close, unref(delay));
};
const close = () => {
_show.value = false;
};
export interface ToastExposed {
open: typeof open;
close: typeof close;
}
defineExpose<ToastExposed>({ open, close });
</script>
<template>
<transition name="toast">
<div v-show="_show" class="toast">{{ _message }}</div>
</transition>
</template>
<style scoped>
.toast {
position: fixed;
z-index: 100;
bottom: 20%;
left: 50%;
padding: 6px 20px;
transform: translateX(-50%);
color: #fff;
background-color: rgba(37, 38, 45, 0.9);
border-radius: 6px;
user-select: none;
}
.toast-enter-active {
animation: fade 0.2s;
}
.toast-leave-active {
animation: fade 0.2s reverse;
}
@keyframes fade {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
在Toast
文件夹下创建index.ts
import { render, createVNode, VNode } from "vue";
import Toast from "./toast.vue";
import type { ToastExposed } from "./toast.vue";
const createToast = () => {
const _vm = createVNode(Toast);
const container = document.createElement("div");
render(_vm, container);
document.body.appendChild(container);
return _vm;
};
let _toast: VNode | null = null;
const useToast = () => {
if (!_toast) _toast = createToast();
const toast = (message: string) => {
(<ToastExposed>_toast?.component?.exposed)?.open(message);
};
return toast;
};
export default useToast;
createVNode
函数以将vue
组件转为vnode
render
函数可以将vnode
渲染到真实DOM
中
2. Usage
<script lang="ts" setup>
import useToast from "@/components/Toast";
const toast = useToast();
</script>
<template>
<button @click="toast('hello world')">button</button>
</template>