运行环境
- 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);}});// 渲染Dialogrender(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 => {// submitconsole.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;}}