需求场景
- 某些情况下,我们要使用一个弹框组件的时候
- 一般都是这样使用:
<button @click="open" />
<el-dialog v-model="dialogVisible">
<span>This is a dialog</span>
</el-dialog>
import { ElDialog } from 'element-plus'
import { ref } from "vue";
const dialogVisible = ref(false)
const open = ()=>{
dialogVisible.value = true
}
- 也就是说,每次想要有一个弹框打开功能
- 都需要引入
ElDialog
、再定义一个弹框打开关闭的变量dialogVisible
- 在点击事件中去更改这个响应式变量
- 每次都要写这么多,那么使用成本就逐渐抬高
- 另外某些场景下,这种用法,并不太合适
- 所以,我们要考虑,能不能通过类似于
alert('消息')
- 这种方式,来直接调用,实现效果?
- 答案是可以的,通过
provide和inject
既可以实现
效果图
第一步,main.js注入dialogService,提供两个函数openDialog, closeDialog
// 引入弹框开关方法
import { openDialog, closeDialog } from './command/dialogService/dialogService.js';
// 使用provide来提供dialogService的开关方法,那么任意子组件,就可以inject使用之了
app.provide('dialogService', { openDialog, closeDialog });
第二步,创建dialogService.js提供openDialog, closeDialog
/command/dialogService/dialogService.js
import { ref, h, createVNode, render } from 'vue';
import { ElDialog } from 'element-plus';
import bodyComponents from './bodyComponents.vue';
import footerComponents from './footerComponents.vue';
// 布尔值变量
const dialogVisible = ref(false);
// 弹框挂载的dom
let container;
const openDialog = (comAttrs, dialogAttrs) => {
dialogVisible.value = true;
if (!container) {
// 创建一个容器dom,并丢进去
container = document.createElement('div');
document.body.appendChild(container);
// 创建虚拟dom节点,并render
const vnode = createVNode(ElDialog, {
title: '我是标题',
width: 800,
// 合并传递进来的el-dialog的属性参数
...dialogAttrs,
modelValue: dialogVisible.value,
}, {
default: () => h(bodyComponents, { comAttrs }),
// 可拓展footer
footer: () => h(footerComponents),
});
render(vnode, container);
}
};
const closeDialog = () => {
dialogVisible.value = false;
if (container) {
render(null, container); // 清除渲染的内容
container.remove();
container = null;
}
};
export { openDialog, closeDialog };
/command/dialogService/bodyComponents.vue
<template>
<div class="dialogBox">
<div>传递的参数:</div>
<pre>
{{ comAttrs }}
</pre
>
</div>
</template>
<script setup>
import { onMounted, onBeforeUnmount } from "vue";
const props = defineProps({
comAttrs: {
type: Object,
default: () => {},
},
});
onMounted(() => {
console.log("onMounted");
});
onBeforeUnmount(() => {
console.log("onBeforeUnmount");
});
</script>
<style lang="less" scoped>
pre {
/* pre宽度不够换行 */
word-wrap: break-word;
white-space: pre-wrap;
/* 加点行高,防止太挤了 */
line-height: 42px;
}
</style>
/command/dialogService/footerComponents.vue
<template>
<div class="f">footer</div>
</template>
<style lang="less" scoped>
.f {
text-align: center;
color: #333;
}
</style>
第三步,通过一个统一inject的函数,便于在某个位置使用
/command/dialogService/useDialog.js
import { inject } from 'vue';
export function useDialog() {
const dialogService = inject('dialogService');
if (!dialogService) {
throw new Error('Dialog service not provided!');
}
return dialogService;
}
第四步,在某个.vue文件中使用
<template>
<div class="canvasWrap">
<button @click="open">打开弹框</button>
</div>
</template>
<script setup>
import { useDialog } from "@/command/dialogService/useDialog.js";
import { ElMessageBox } from "element-plus";
const { openDialog, closeDialog } = useDialog();
const open = () => {
const comAttrs = {
name: "孙悟空",
age: 500,
home: "花果山",
};
const dialogAttrs = {
title: "弹框的title",
width: 320,
showClose: true,
draggable: true,
overflow: true,
closeOnClickModal: false,
beforeClose(done) {
ElMessageBox.confirm("Are you sure to close this dialog?")
.then(() => {
done();
closeDialog()
})
.catch(() => {
// catch error
});
},
};
openDialog(comAttrs, dialogAttrs);
};
</script>
至此,我们就可以实现类似于alert('xxx')
的形式,打开弹框了...
完整代码github
github: https://github.com/shuirongshuifu/vue3-echarts5-example
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。