在做后台项目时候,使用声明式组件比较多,就是写一个.vue
文件,在里面写 template
、script
、style
哪里需要,就在哪里导入。
而对于前台项目而言,我们期望可以直接通过方法的形式调用,利用函数式组件,在封装时除了要写.vue
,还要多一个手动渲染和卸载的步骤。我们可以通过 h
函数可以生成一个vnode
,该vnode
利用render
函数渲染和卸载。
<template>
<!-- 声明式组件 -->
<el-row class="mb-4">
<el-button type="primary" @click="open">Primary</el-button>
<el-button type="success">Success</el-button>
</el-row>
</template>
<script lang="ts" setup>
// 函数式组件
import { ElMessage } from 'element-plus'
// 通常是在某个交互完成时触发
const open = () => {
ElMessage('this is a message.')
}
</script>
1.1. h 函数
Vue中,提供了一个h()
函数用于创建 vnodes
。创建虚拟节点时会多种不同情况,比如传入标签名和属性,就会创建一个标签的虚拟节点,传入组件名和属性,就会创建一个组件的虚拟节点。
h()
接收三个参数(要渲染的dom,attrs 对象,子元素)
h()
有一个更准确的名称是 createVnode()
,考虑到多次使用,一个简短的名字会更省力。
import { h } from 'vue'
// 使用 h 创建普通标签
const vnode1 = h('div', { class: 'bar', innerHTML: 'hello' })
// vnode1 等同于 <div class="bar">hello</div>
// 使用 h 创建组件
const vnode2 = h(myComponent, {
//组件的属性
title: '测试'
})
// vnode2 等同于 <myComponent title="测试"/>
1.2. render 函数
render()
接收标签或者组件的 vnode
,将其渲染成为真实 DOM
,并挂载到一个指定的父节点上。
import { h, render } from 'vue'
render(vnode2, document.body)
render(null, document.body) // 当第1个参数为null时,相当于从父节点上移除此组件。
1.3. confirm组件
以实现 comfirm
组件为例,具体实现逻辑如下:
- 创建一个
confirm
组件 - 创建一个
comfirm.js
模块,该模块返回一个promise
- 同时利用
h()
生成confirm.vue
的vode
- 最后利用
render
函数,渲染vnode
到body
中
1.3.1. 构建 confirm.vue 组件
<script setup>
import { ref, onMounted } from 'vue'
// 因为将来 confirm 组件是以方法调用的形式展示,所以我们需要手动导入需要使用到的其他通用组件
import mButton from '@/libs/button/index.vue'
const props = defineProps({
// 标题
title: {
type: String
},
// 描述
content: {
type: String,
required: true
},
// 取消按钮文本
cancelText: {
type: String,
default: '取消'
},
// 确定按钮文本
confirmText: {
type: String,
default: '确定'
},
// 取消按钮事件
cancelHandler: {
type: Function
},
// 确定按钮事件
confirmHandler: {
type: Function
},
// 关闭 confirm 的回调
close: {
type: Function
}
})
// 控制显示处理
const isVisible = ref(false)
/**
* confirm 展示
*/
const show = () => {
isVisible.value = true
}
/**
* 处理动画 (render 函数的渲染,会直接进行)
*/
onMounted(() => {
show()
})
/**
* 取消事件
*/
const onCancelClick = () => {
if (props.cancelHandler) {
props.cancelHandler()
}
close()
}
/**
* 确定事件
*/
const onConfirmClick = () => {
if (props.confirmHandler) {
props.confirmHandler()
}
close()
}
// 关闭动画处理时间
const duration = '0.5s'
/**
* 关闭事件,保留动画执行时长
*/
const close = () => {
isVisible.value = false
// 延迟一段时间进行关闭
setTimeout(() => {
if (props.close) {
props.close()
}
}, parseInt(duration.replace('0.', '').replace('s', '')) * 100)
}
</script>
<template>
<!-- 基于 tailwindcss 创建对应样式 -->
<div>
<!-- 蒙版 -->
<transition name="fade">
<div
v-if="isVisible"
@click="close"
class="w-screen h-screen bg-zinc-900/80 z-40 fixed left-0 top-0"
></div>
</transition>
<!-- 内容 -->
<transition name="up">
<div
v-if="isVisible"
class="w-[80%] fixed top-1/3 left-[50%] translate-x-[-50%] z-50 px-2 py-1.5 rounded-sm border dark:border-zinc-600 cursor-pointer bg-white dark:bg-zinc-800 xl:w-[35%]"
@click="close"
>
<!-- 标题 -->
<div class="text-lg font-bold text-zinc-800 dark:text-zinc-200 mb-2">{{ title }}</div>
<!-- 文本 -->
<div class="text-base tex-zinc-800 dark:text-zinc-200 mb-2">{{ content }}</div>
<!-- 按钮 -->
<div class="flex justify-end">
<m-button type="info" class="mr-2" @click="onCancelClick">{{ cancelText }}</m-button>
<m-button type="primary" @click="onConfirmClick">{{ confirmText }}</m-button>
</div>
</div>
</transition>
</div>
</template>
<style lang="scss" scoped>
.fade-enter-active,
.fade-leave-active {
transition: all v-bind(duration);
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.up-enter-active,
.up-leave-active {
transition: all v-bind(duration);
}
.up-enter-from,
.up-leave-to {
opacity: 0;
transform: translate3d(-50%, -100%, 0);
}
</style>
1.3.2. 创建 confirm.js 模块
import { h, render } from 'vue'
import confirmComponent from './confirm.vue'
/**
* @param {*} title 标题
* @param {*} content 内容
* @param {*} cancelText 取消文本
* @param {*} confirmText 确认文本
*/
export const confirmBox = (title, content, cancelText, confirmText) => {
return new Promise((resolve, reject) => {
// 不传入标题,只传入内容时
if (title && !content) {
content = title
title = ''
}
// 取消按钮事件
const cancelHandler = () => {
reject(new Error('取消按钮点击'))
}
// 确定按钮事件
const confirmHandler = () => {
resolve()
}
// 关闭弹层事件
const close = () => {
render(null, document.body)
}
// 1. 生成 vnode
const vnode = h(confirmComponent, {
title,
content,
cancelText,
confirmText,
cancelHandler,
confirmHandler,
close
})
// 2. render 渲染
render(vnode, document.body)
})
}
1.3.3. 触发 comfirm 组件
import { confirmBox } from './confirm.js'
const onDeleteAllClick = () => {
confirmBox('要删除所有历史记录吗?').then(() => {
// 点击确定后执行事件
...
})
.catch(()=>{
// 点击取消后执行事件
...
})
}
学子资料:点此下载