Vue3 服务式(函数式)组件
之前在
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, .9);
border-radius: 6px;
user-select: none;
}
.toast-enter-active {
animation: fade .2s;
}
.toast-leave-active {
animation: fade .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>
为之则易,不为则难。