4

vue3优雅的使用useDialog

 1 month ago
source link: https://www.fly63.com/article/detial/12690
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在日常开发时,弹窗是一个经常使用的功能,而且重复性极高,你可能会遇到下面这些问题:1、一个页面内多个弹窗, 要维护多套弹窗状态,看的眼花缭乱2、弹窗内容比较简单,声明变量 + 模板语法的方式写起来比较麻烦

关于这些问题, 我首先想到的是应该弄一个即用即走的Dialog,不用去单独维护它的状态,使用Dialog({ xxx })这种形式去调用它,例如下面这种配置的方式:

Dialog({
title: 'xxx',
render: () => xxx
})

其中render可以是一个html字符串,也可以是jsx(需要配置对jsx的支持),这样可以对内容区域实现自定义。

通过配置项调用

各大主流的ui库基本都实现了这种调用方式:

  • Ant Design vue的 Modal
  • Element-plus的 MessageBox

之前没有注意到Element-plus的MessageBox可以使用jsx,大部分场景下,用它来代替Dialog还是很方便的。示例代码:

<script lang="jsx" setup>
import { reactive, ref } from 'vue';
import { ElMessageBox, ElForm, ElFormItem, ElInput } from 'element-plus'

const formRef = ref(null)
const form = reactive({ height: '', width: '' })
const rules = reactive({
height: {
required: true,
trigger: 'blur'
},
width: {
required: true,
trigger: 'blur'
}
})

function openMessageBox() {
ElMessageBox({
title: 'Message',
showCancelButton: true,
// message如果不是函数形式 绑定ref会失败
message: () =>
<ElForm
ref={formRef}
model={form}
rules={rules}
>
<ElFormItem label="height" prop="height">
<ElInput v-model={form.height}></ElInput>
</ElFormItem>
<ElFormItem label="width" prop="width">
<ElInput v-model={form.width}></ElInput>
</ElFormItem>
</ElForm>
,
beforeClose: (action, instance, done) => {
console.log(action, instance)
formRef.value && formRef.value.validate(status => {
console.log('校验状态: ', status)
if (status || action==='cancel') done()
})
}
})
}
</script>

<template>
<div>
<button @click="openMessageBox">
打开messagebox
</button>
</div>
</template>

vueuse的createTemplatePromise

如果你不想使用jsx,而是想使用模板,vue的hooks工具库vueuse中提供了 createTemplatePromise 这个函数用来创建对话框、模态框、Toast 等,并且完全使用的是template的方式,因此自定义程度更高,并且没有任何额外成本(不需要jsx)。下面是一个createTemplatePromise结合el-dialog的例子(当然也可以结合其它的dialog或者自定义dialog):

<script lang="jsx" setup>
import { createTemplatePromise } from '@vueuse/core'
import { ElDialog, ElButton } from 'element-plus'

const TemplatePromise = createTemplatePromise()

async function open(idx) {
console.log(idx, 'Before')
const result = await TemplatePromise.start('Title', `Hello ${idx}`)
console.log(idx, 'After', result)
}

function asyncFn() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('ok')
}, 1000)
})
}
</script>

<template>
<div>
<button @click="open(1); open(2)">
打开两个弹框
</button>
</div>
<TemplatePromise v-slot="{ resolve, args, isResolving }">
<el-dialog :modelValue="true" :title="args[0]">
<div>Dialog {{ args[1] }}</div>
<p>可以打开控制台查看logs</p>
<div class="flex gap-2 justify-end">
<el-button @click="resolve('cancel')">
取消
</el-button>
<el-button type="primary" :disabled="isResolving" @click="resolve(asyncFn())">
{{ isResolving ? 'loading...' : '确认' }}
</el-button>
</div>
</el-dialog>
</TemplatePromise>
</template>

Promise化Dialog的优点

这样有小伙伴可能会说, 这看起来和原来用dialog也没有很大区别啊, 也要写模版 + 函数方法. 那么让dialog变成这样有什么好处呢?

1、最大的好处是弹窗变得可编程了, 通过函数调用的方式来控制UI. 不用再单独声明变量控制显隐, 也不用单独再去控制按钮的禁用、loading等状态. 例如以上的例子中, 我们可以轻松的处理button的loading状态(不用再额外声明变量), 用Promise让Dialog的UI和状态实现了內聚.

2、相比第一种方式, 对UI的可自定义程度更高.

demo地址

代码地址:https://github.com/plutda/functional-dialog

在线预览地址:https://plutda.github.io/functional-dialog

作者:隔壁老王z
https://juejin.cn/post/7293173815181738022

链接: https://www.fly63.com/article/detial/12690


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK