无论是使用第三方组件库还是自己封装组件,有一类组件与众不同,它们是函数式/服务式组件,例如消息组件、通知组件、加载组件等等。
以 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 美化
定义函数
然后暴露接口(套路):
- 创建位于 body 的 DOM 挂载点(使用
document.createElementdocument.body.appendChild) - 创建虚拟 DOM(使用
h()) - 将虚拟 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)
参考文章:
- 渲染机制 | Vue.js (vuejs.org)
- 渲染函数 & JSX | Vue.js (vuejs.org)
- 依赖注入 | Vue.js (vuejs.org)
- Vue3封装 Message消息提示实例函数 - 掘金 (juejin.cn)
- Vue3 消息提示组件封装 - 掘金 (juejin.cn)
- vue3 函数调用自定义loading组件 - 掘金 (juejin.cn)
- Vue3应用API——设置全局属性(app.provide与app.config.globalProperties的区别) - 掘金 (juejin.cn)
- Vue3的全局变量app.config.globalProperties的使用 - 掘金 (juejin.cn)