Component 组件的函数调用

1
2
3
4
5
6
7
8
9
10
<van-popup v-model:show="visiblePopup">
<Comp v-if="visiblePopup" :some-props="someProps" />
</van-popup>

<script lang="ts" setup>
import {ref, reactive} from 'vue'

const visiblePopup = ref(false)
const someProps = reactive()
</script>

像上面这样的模板式弹框代码,在项目中是最常见不过的,以 Component 方式调用弹框,如果代码组织不好,会使得 template 变得异常凌乱,更重要的是其中充斥着诸如 visiblePopupsomeProps 等这样的中间变量,显得尤为不优雅。而像下面这样以函数方式调用则简洁得多。

1
2
3
const handleOpenComp = () => {
showPopup(comp, someProps)
}

很多 UI 库均提供了弹框的函数调用,比如 Vant UI 的 Dialog,Arco Design 的 Modal,如果没有提供,则需手动改造。以 Vant UI 的 Popup 为例,将其由 Component 组件转换为函数组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { createVNode, render, h, nextTick } from 'vue'
import type { Component, VNode } from 'vue'
import type { PopupProps } from 'vant'
import Popup from './popup.vue'

interface Options extends PopupProps {
title?: string
showConfirmButton?: boolean
showCancelButton?: boolean
onBeforeConfirm?: () => Promise<void>
}

type CloseType = 'close' | 'cancel' | 'confirm'

interface ShowPopup {
(comp: Component, props: Record<string, any> | null, options: Partial<Options>): Promise<void>
}

export const showPopup: ShowPopup = (comp, props = {}, options = {}) => {
return new Promise((resolve, reject) => {
const container = document.createElement('div')
document.body.appendChild(container)

const closePopup = (type: CloseType) => {
render(null, container)
document.body.removeChild(container)
type === 'confirm' ? resolve() : reject(type)
}

const onUpdateShow = async (value: boolean, type: CloseType) => {
await nextTick()
if (!value) {
closePopup(type)
}
}

const vnode = createVNode(
Popup,
{
...options,
show: true,
'onUpdate:show': onUpdateShow
},
{
default: (): VNode => h(comp, { ...props, closePopup })
}
)

render(vnode, container)
})
}