自定义组件 - Message 消息提示

目标:使用 Vue2 的语法,简单实现一个类似 element-ui - Message 消息提示 的组件。

组成

点击上面的链接,可以看到展示效果,由以下几个部分组成:

  • 消息类型
  • icon 图标
  • 消息内容

另外还有2点

  • 消息提示是水平居中的,至于垂直方向的位置,自由度比较高,看具体情况。
  • 消息提示关闭后,设置可以执行一个传入回调函数。

实现

分为下面几个部分。

Icon 组件

这里只是简单实现,详细的实现可以参考这里TODU。

<template>
  <i class="iconfont" :class="'icon-' + name"></i>
</template>

<script>
export default {
      
      
  props: {
      
      
    name: String,
  },
};
</script>
<style scoped>
/* 包含了所有的 class */
@import "//at.alicdn.com/t/c/font_4268117_e989xit5r2k.css";
</style>

使用

<Icon name="success" />

CSS Modules

可以在 js 中导入 css 来使用。参考这篇文章

用于导入 Message 组件的 css。

得到组件渲染的DOM

用于获取 Icon 组件的 DOM 元素,和消息内容进行拼接。具体参考这篇文档

其他注意点

1,如何手动触发浏览器强行渲染(reflow重排)

读取 DOM 元素的尺寸可位置即可。比如 clientHeight

2,过渡事件

transtionend 事件会在过渡完成后触发。addEventListener 也可以通过 once 来指定只执行一次。

最终实现

showMessage.js

import Vue from "vue";
import Icon from "@/components/icon";
import styles from "./showMessage.module.less";

/**
 * 弹出消息
 * @param {String} content 内容
 * @param {String} type 主题  info  error  success  warn
 * @param {Number} duration 显示时间, 毫秒
 * @param {HTMLElement} container 容器,消息在该容器内垂直水平居中;不传则显示到页面正中
 */
export default function (options = {
     
     }) {
    
    
  const content = options.content || "";
  const type = options.type || "info";
  const duration = options.duration || 2000;
  const container = options.container || document.body;
  // 创建消息元素
  const div = document.createElement("div");
  const iconDom = getComponentRootDom(Icon, {
    
    
    name: type,
  });
  div.innerHTML = `<span class="${
      
      styles.icon}">${
      
      iconDom.outerHTML}</span><div>${
      
      content}</div>`;
  // 设置样式
  const typeClassName = styles[`message-${
      
      type}`]; //类型样式名
  div.className = `${
      
      styles.message} ${
      
      typeClassName}`;
  // 将div加入到容器中

  // 容器的 position 是否改动过
  if (options.container && getComputedStyle(container).position === "static") {
    
    
    container.style.position = "relative";
  }

  container.appendChild(div);
  // 浏览器强行渲染
  div.clientHeight; // 导致reflow

  // 回归到正常位置
  div.style.opacity = 1;
  div.style.transform = `translate(-50%, -50%)`;

  // 等一段时间,消失
  setTimeout(() => {
    
    
    div.style.opacity = 0;
    div.style.transform = `translate(-50%, -50%) translateY(-25px)`;
    div.addEventListener(
      "transitionend",
      function () {
    
    
        div.remove();
        // 运行回调函数
        options.callback && options.callback();
      },
      {
    
     once: true }
    );
  }, duration);
}

// 获取某个组件渲染的Dom根元素
function getComponentRootDom(comp, props) {
    
    
  const vm = new Vue({
    
    
    render: (h) => h(comp, {
    
     props }),
  });
  vm.$mount();
  return vm.$el;
}

showMessage.module.less

.message {
    
    
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) translateY(25px);
  z-index: 999;
  border: 1px solid;
  border-radius: 5px;
  padding: 10px 30px;
  line-height: 2;
  display: flex;
  align-items: center;
  transition: 0.4s;
  opacity: 0;
  white-space: nowrap;
  &-info {
    
    
    background: #edf2fc;
    color: #909399;
  }
  &-success {
    
    
    background-color: #f0f9eb;
    border-color: #e1f3d8;
    color: #67c23a;
  }
  &-warn {
    
    
    background-color: #fdf6ec;
    border-color: #faecd8;
    color: #e6a23c;
  }
  &-error {
    
    
    background-color: #fef0f0;
    border-color: #fde2e2;
    color: #f56c6c;
  }
}

.icon {
    
    
  font-size: 20px;
  margin-right: 8px;
}

使用

可以挂在到 Vue 原型上方便全局使用

import Vue from "vue";
import App from "./App.vue";
import showMessage from "./utils/showMessage";
Vue.prototype.$showMessage = showMessage;

new Vue({
    
    
  render: (h) => h(App),
}).$mount("#app");
handleClick() {
    
    
  this.$showMessage({
    
    
    content: "错误消息",
    type: "error",
    duration: 1500,
  });
}

以上。

猜你喜欢

转载自blog.csdn.net/qq_40147756/article/details/133282216
今日推荐