Vue3:定义一个简单的函数式组件

528 阅读2分钟

无论是使用第三方组件库还是自己封装组件,有一类组件与众不同,它们是函数式/服务式组件,例如消息组件、通知组件、加载组件等等。

以 ElementPlus 组件库为例,大部分组件都是声明式的。例如:

<el-button type="primary" @click="handleClick">点击</el-button>

<el-input v-model="username" />

声明式组件和函数式组件最大的不同在于渲染组件的方式。前者是声明式的,后者需要通过调用 API 的方式来渲染组件。例如:

import { ElMessage } from 'element-plus'

// 通常在某个交互完成时触发
const handleClick = () => {
  ElMessage.success('成功')
  // ElMessage.error('错误')
}

另外一点是,函数式组件通常被挂载到 body 上。而且一般全局只维护一个或多个实例。例如通知组件可能存在多个通知消息,而确认框组件可能全局只会渲染一个。

这里来定义一个 alert 函数式组件

定义组件

首先定义一个 alert 组件,src/components/alert/alert.vue

<template>
  <div v-show="isVisable" class="fixed opacity-50 px-4 py-2 rounded-2xl left-8 top-8 text-2xl text-warning-content bg-warning">
    {{ message }}
  </div>
</template>

<script setup>
import { onMounted, ref } from 'vue'

const props = defineProps({
  message: String,
})
const isVisable = ref(false)

// 当挂载 alert 时,显示
onMounted(() => {
  isVisable.value = true
})
</script>

使用了 tailwindcss 进行 css 美化

定义函数

然后暴露接口(套路):

  1. 创建位于 body 的 DOM 挂载点(使用 document.createElement document.body.appendChild
  2. 创建虚拟 DOM(使用 h()
  3. 将虚拟 DOM 渲染到 DOM 挂载点(使用 reader()

src/components/alert/index.vue

import { render, h, createVNode } from 'vue'
import alertComponent from '@/components/alert.vue'

// Step 1:创建一个 DOM 挂载点,并设定 Class 名, 挂载到 body
const alertContainer = document.createElement('div')
alertContainer.className = 'alert-container'
document.body.appendChild(alertContainer)

export default alert = (message, duration = 2000) => {
  // Step 2:创建一个虚拟 DOM
  const alertVnode = createVNode(alertComponent, {message: message})

  // Step 3:将虚拟 DOM 渲染到实际的一个 DOM 中
  render(alertVnode, alertContainer)

  // 默认 2 秒后关闭通知
  setTimeout(() => render(null, alertContainer), duration)
}

此处不懂的,请参考文章末尾的参考链接

依赖注入 & 全局属性

有两种安装到全局的方法:依赖注入和加到全局属性

依赖注入,首先在src/components/index.js中定义一个安装函数,利用 provide() 注入相应的方法

import alert from "./alert"
// 这里统一安装全局组件

export default (app) => {
  app.provide('alert', alert) // key any
}

利用 inject() 获取注入的方法

import { inject, onMounted } from 'vue'

// 组件中使用 inject 通过 key 接收全局属性
const $alert = inject("alert")
onMounted(() => {
  $alert('Alert', 2000)
})

全局属性,首先在src/components/index.js中定义一个安装函数,使用app.config.globalProperties API来安装一个全局属性

import alert from "./alert"
export default (app) => {
  app.config.globalProperties.$alert = alert
}

利用 getCurrentInstance() 获取 proxy,再通过 proxy 获取相应的属性

import { getCurrentInstance, ref } from 'vue'
const { proxy } = getCurrentInstance()
proxy.$alert('Alert', 2000)

参考文章: