需求场景

  • 某些情况下,我们要使用一个弹框组件的时候
  • 一般都是这样使用:
<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


水冗水孚
1.1k 声望589 粉丝

每一个不曾起舞的日子,都是对生命的辜负