目标:使用 Vue2 的语法,简单实现一个类似 element-ui - Message 消息提示 的组件。
点击上面的链接,可以看到展示效果,由以下几个部分组成:
另外还有2点
分为下面几个部分。
这里只是简单实现,详细的实现可以参考这里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" />
可以在 js 中导入 css 来使用。参考这篇文章
用于导入 Message
组件的 css。
用于获取 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 = `${styles.icon}">${iconDom.outerHTML}${content}`;
// 设置样式
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,
});
}
以上。