Skip to content

1.二次封装防止多次重复弹窗

@/utils/resetMessage.ts

ts
// import { ElMessage } from "element-plus"; //按需引入时,这里不需要引入,否则样式失效

let messageInstance: any | null = null; //定义一个变量

// 这种方式失败了,不知道为什么
// const resetMessage = (options: {}) => {
//   if (messageInstance && messageInstance.closeAll) {
//     messageInstance.closeAll();
//   }
//   //@ts-ignore
//   // return (messageInstance = ElMessage(options));
//   messageInstance = ElMessage(options);
// };

//封装ElMessage的函数:
const resetMessage = (options) => {
  //如果实例已经被创建了,并且实例没有被关闭
  if (messageInstance && messageInstance.close) {
    messageInstance.close(); //那么就直接关闭
  }
  //@ts-ignore //必须加这个注释,方式ts检查报错
  messageInstance = ElMessage(options); //调用原本的El组件
};

//识别resetMessage函数的参数类型,以适应.success等函数
["error", "success", "info", "warning"].forEach((type) => {
  resetMessage[type] = (options: any) => {
    if (typeof options === "string") {
      options = {
        message: options, //将传入的参数赋值给message属性
      };
    }
    options.type = type; //将后面.success赋值给type属性
    return resetMessage(options); //调用上面的函数
  };
});
export const Message = resetMessage; //这里导出的是const resetMessage这个函数

2.全局绑定ElMessage并用scss自定义样式

@/plugins/elementPlus/index.ts

ts
// src/plugins/elementPlus/index.ts

import type { App } from "vue";

// 需要全局引入一些组件,如ElScrollbar,不然一些下拉项样式有问题
// 因为配置了按需引入,所以这里一定不要再引入了,否则样式会有问题!
// import { ElLoading, ElScrollbar, ElMessage } from "element-plus";

//改为引入二次封装的ElMessage,禁止弹窗多次出现
import { Message } from "@/utils/resetMessage";

//@ts-ignore
const plugins = [ElLoading];

//@ts-ignore
const components = [ElScrollbar];

//定义函数对app进行封装,绑定一些二次封装的El组件
export const setupElementPlus = (app: App<Element>) => {
  plugins.forEach((plugin) => {
    app.use(plugin);
  });

  components.forEach((component) => {
    app.component(component.name, component);
  });

  const ElMessageCfg = { duration: 2000, customClass: "globalElMessageStyle" }; //前面为多长时间消失,后面为样式class
  //将二次封装的El组件绑定到全局
  app.config.globalProperties.$ElMessage = (msg) => {
    //@ts-ignore
    return Message({ message: msg, ...ElMessageCfg }); //传入消息msg和样式等信息参数
  };
  app.config.globalProperties.$ElMessage.success = (msg) => {
    //@ts-ignore
    return Message.success({ message: msg, ...ElMessageCfg });
  };
  app.config.globalProperties.$ElMessage.warning = (msg) => {
    //@ts-ignore
    return Message.warning({ message: msg, ...ElMessageCfg });
  };
  app.config.globalProperties.$ElMessage.info = (msg) => {
    //@ts-ignore
    return Message.info({ message: msg, ...ElMessageCfg });
  };
  app.config.globalProperties.$ElMessage.error = (msg) => {
    //@ts-ignore
    return Message.error({ message: msg, ...ElMessageCfg });
  };
};

@/styles/component.scss

scss
.globalElMessageStyle {
  z-index: 3000 !important;
  border: 1px saddlebrown solid !important; //必须加!important
  // position: absolute;
  // left: 500px;
}

@/styles/index.scss

scss
@import "./component.scss";

3.定义全局获取vm实例的方法

@/utils/useCurrentInstance.ts

ts
//注意:appContext.config.globalProperties与proxy基本上是等价的,这里我们选用globalProperties
import { ComponentInternalInstance, getCurrentInstance } from "vue";
export default function useCurrentInstance() {
  const { appContext } = getCurrentInstance() as ComponentInternalInstance;
  const globalProperties = appContext?.config.globalProperties; //这里最好加一个?,因为返回类型可能存在null,所以要在此处添加断言,在proxy后面添加?来过滤null的结果,否则到了线上可能会报错
  return {
    globalProperties,
  };
}

4.对全局的ElMessage进行使用

1.main.ts

ts
import { createApp } from "vue";
import App from "./App.vue";
import router from "@/router";
import pinia from "@/store";

// 引入element-plus的封装方法
import { setupElementPlus } from "@plugins/elementPlus/index";

// 引入全局样式(定义了ElMessage的样式
import "@/styles/index.scss";

// 原来的创建实例的方法:
// const app = createApp(App);
// app.use(router);
// app.use(pinia);
// app.provide("$message", ElMessage);
// app.mount("#app");

//现在的创建实例的方法:
const setupAll = async () => {
  const app = createApp(App);
  setupElementPlus(app); //相当于进入给app加工一下,没什么太大的区别
  app.use(router);
  app.use(pinia);
  app.mount("#app");
};

setupAll(); //调用创建实例

2.HelloWorld.vue

vue
<script setup lang="ts">
import { h } from "vue";
// import { ElMessage } from "element-plus";
// import { getCurrentInstance } from "vue";
// import { Message } from "@/utils/resetMessage";
import useCurrentInstance from "@/utils/useCurrentInstance";

// 未定义全局方法时:
// const { proxy } = getCurrentInstance() as any; //这里也必须要as any,否则下面在.属性的时候会报错!
// const { ctx } = getCurrentInstance() as any; //这里必须要as any

//定义了全局方法之后:
const { globalProperties } = useCurrentInstance();
    
//建议用箭头函数写法来定义函数
const showMessage = () => {
  //测试普通的ElMessage:
  // ElMessage.success("你好");
  // ElMessage.error("你好");
    
  //测试二次封装El的resetMessage:
  // Message.success("你好");
  // Message.error("你好");
  // Message({
  //   message: "登录注册成功",
  //   type: "success",
  // });

  //测试传入自定义Vnode时,普通的和封装的区别:
  //原来的写法:
  //@ts-ignore
  // ElMessage({
  //   message: h('p', null, [
  //     h('span', null, 'Message can be '),
  //     h('i', { style: 'color: teal' }, 'VNode'),
  //   ]),
  // })
  //现在的写法:不用写k-v,直接写v即可!
  // proxy.$ElMessage(
  //   h("p", null, [
  //     h("span", null, "Message can be "),
  //     h("i", { style: "color: teal" }, "VNode"),
  //   ])
  // );
    
  // 测试非全局获取vm实例:
  // proxy.$ElMessage.success("成功");
  // proxy.$ElMessage.error("失败"); //只会显示最后一个

  // 测试全局获取vm实例:
  globalProperties.$ElMessage.success("成功");
  globalProperties.$ElMessage.error("失败");

};
</script>