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>
posted @ 2022-07-07 18:55  demo_you  阅读(3223)  评论(2编辑  收藏  举报