运行环境
- vue3
- vite
- ts
- element-plus
开发与测试
1. 使用h、render函数创建Dialog
- 建议可在plugins目录下创建dialog文件夹,创建index.ts文件,代码如下
import { h, render } from "vue";
/**
* 函数式弹窗
* @param component 组件
* @param options 组件参数
* @returns
*/
function createDialog(component: any, options: any) {
return new Promise((resolve, reject) => {
// 创建一个div节点
const mountNode = document.createElement("div");
// 将div节点拼接到Dom的body节点下
document.body.appendChild(mountNode);
// 使用h函数创建节点
const vNode = h(component, {
...options,
// 注意: vue子组件emit回调事件名称必须以on开头
onSubmit: data => {
resolve(data);
// 移除节点
document.body.removeChild(mountNode);
},
onCancel: data => {
reject(data);
// 移除节点
document.body.removeChild(mountNode);
}
});
// 渲染Dialog
render(vNode, mountNode);
});
}
export default createDialog;
2. 全局挂载函数式弹窗
- 在main.ts中引入弹窗,并挂载在app上
// 引入函数式弹窗
import Dialog from "@/plugins/dialog";
const app = createApp(App);
// 挂载到app
app.config.globalProperties.$dialog = Dialog;
3. 测试
3.1 创建一个弹窗组件 testDialog.vue
<template>
<el-dialog v-model="dialogVisible" title="测试函数式弹窗" width="50%">
<span>{{ props.content }}</span>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCancel">Cancel</el-button>
<el-button type="primary" @click="handleSubmit"> Submit </el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { reactive, toRefs } from "vue";
// 注意: 需要按需引入使用到的第三方UI组件
import { ElDialog, ElButton } from "element-plus";
const props = withDefaults(
defineProps<{
show?: boolean; // moadl开关
content?: string; // 内容
}>(),
{}
);
const emits = defineEmits(["submit", "cancel"]);
const state = reactive({
dialogVisible: props.show
});
const { dialogVisible } = toRefs(state);
/** submit */
const handleSubmit = () => {
// 回调
emits("submit", { action: "submit", msg: "submit back" });
// 关闭弹窗
dialogVisible.value = false;
};
/** cancel */
const handleCancel = () => {
// 回调
emits("cancel", { action: "cancel", msg: "cancel back" });
// 关闭弹窗
dialogVisible.value = false;
};
</script>
3.2 函数式调用弹窗
<template>
<!-- 动态函数式弹窗 -->
<div class="test_dialog">
<el-button @click="openModal">调用函数式弹窗</el-button>
</div>
</template>
<script lang="ts" setup>
import { getCurrentInstance } from "vue";
import TestDialog from "./testDialog.vue";
// 通过全局的上下文拿到 proxy 属性
const { proxy } = getCurrentInstance();
// 调用函数式弹窗
const openModal = () => {
// 调用弹窗
proxy
.$dialog(TestDialog, {
show: true,
content: "调用弹窗成功了!"
})
.then(res => {
// submit
console.log(res);
})
.catch(error => {
// cancel 回调
console.log(error);
});
};
</script>
<style lang="scss" scoped>
.test_dialog {
padding: 50px;
}
</style>
3.3 测试效果
问题
-
非原生的html元素无法渲染,如elements-plus组件无法在弹窗渲染
因为使用h函数无法渲染第三方UI,需要在弹窗中单独引入,如上面测试代码使用的element-plus的modal和button都需要按需引入一次。如果没有引入弹窗都不会show出来,控制台会给于警告如下截图,通过这个截图也可以看到,h函数是帮我们将弹窗组件拼接到了DOM中,组件的参数一并拼接了进去,与传统的调用方式近似。
-
在调用dialog的代码中,ts会有代码警告
可以全局申明下挂载的dialog,可直接在main.ts添加下面的申明
// 全局申明下$dialog,可以去除调用时ts的警告
declare module "vue" {
export interface ComponentCustomProperties {
$dialog: any;
}
}