本文的研究思路是通过阅读Element源码,然后自动动手一步一步编写组件,完善其对应功能。
准备工作
准备工作搞起来,先写一个测试页MessageShownPage
<template>
<div>
内容
<el-message />
</div>
</template>
<script>
import ElMessage from '../../components/Message/message.vue'
export default {
name: 'MessageShownPage',
components: {
ElMessage
}
}
</script>
<style>
</style>
基本实现
接下来组件进行message最简单的实现:
<template>
<transition name="el-message-fade">
<div
:class="[
'el-message',
type ? `el-message--${ type }` : '',
]"
:style="positionStyle"
v-show="visible"
>
<i :class="typeClass"></i>
<slot>
<p class="el-message__content">{{ message }}</p>
</slot>
</div>
</transition>
</template>
<script>
const typeMap = {
success: 'success',
info: 'info',
warning: 'warning',
error: 'error'
};
export default {
data() {
return {
visible: true,
message: '这是一条消息提示',
type: 'info',
verticalOffset: 20,
};
},
computed: {
typeClass() {
return this.type
? `el-message__icon el-icon-${ typeMap[this.type] }`
: '';
},
positionStyle() {
return {
'top': `${ this.verticalOffset }px`
};
}
}
}
</script>
效果如下:
一个静态的,在页面上的message消息。
看一下它的样式:
.el-message {
min-width: 380px;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid #ebeef5;
position: fixed;
left: 50%;
top: 20px;
transform: translateX(-50%);
background-color: #edf2fc;
transition: opacity .3s,transform .4s,top .4s;
overflow: hidden;
padding: 15px 15px 15px 20px;
display: flex;
align-items: center;
}
可以看到message是通过 position: fixed;定位到页面上。
实现3秒后自动消失效果
我们的组件是包裹在transition name="el-message-fade"标签下。下面的div 的 v-show="visible"控制组件显示隐藏的时候,会触发transition的动画。所以我们定义个定时器,3秒后控制visible = false即可。
methods: {
close() {
this.closed = true;
this.visible = false
},
startTimer() {
// duration是3000
if (this.duration > 0) {
this.timer = setTimeout(() => {
if (!this.closed) {
this.close();
}
}, this.duration);
}
},
},
mounted() {
this.startTimer();
},
这样,我们的组件就会3秒后自动消失了。
通过指令方式调用message
现在Vue上挂载message对象:
import messageService from './components/Message/service.js'
Vue.prototype.$message = messageService;
这样我们就可以在组件中使用this.$message()了。接下来实现service.js
import Vue from 'vue';
import Main from './message.vue';
let MessageConstructor = Vue.extend(Main);
let instance;
const Message = function(options) {
// 调用this.$message时传递进来的参数
options = options || {};
// 如果参数是个string,直接赋值给message
if (typeof options === 'string') {
options = {
message: options
};
}
// 构造message实例
instance = new MessageConstructor({
data: options
});
instance.$mount();
// 将dom添加到网页上
document.body.appendChild(instance.$el);
instance.visible = true;
return instance;
}
export default Message;
我们在测试页面里写:
mounted() {
setTimeout(() => {
this.$message('就这?就这?')
}, 1000);
},
1秒后,message就出现了,3秒后,message消失。达到了通过指令方式调用message。
message不同状态
message有’success’, ‘warning’, ‘info’, 'error’四种状态。
this.$message({
message: '恭喜你,这是一条成功消息',
type: 'success'
})
通过上面的代码,我们就可以调出一个成功提示。还有下面一种调用方式。
this.$message.error('错了哦,这是一条错误消息');
直接点出error方法,就可以调用一个错误提示。我们来支持一下。在service.js中添加代码:
const status = ['success', 'warning', 'info', 'error']
status.forEach(type => {
Message[type] = options => {
if (typeof options === 'string') {
options = {
message: options
};
}
options.type = type;
return Message(options);
};
});
这样我们的$message就多了四个方法。显示的效果与直接传递options对象效果一致。
效果如下:
可关闭
调用方式:
this.$message({
showClose: true,
message: '这是一条消息提示'
});
我们在组件中加入代码:
<i v-if="showClose" class="el-message__closeBtn el-icon-close" @click="close"></i>
效果如下:
close方法我们先前已经实现。
文字居中
调用方式:
this.$message({
message: '居中的文字',
center: true
});
通过center控制样式即可实现。在组件div的样式中添加center ? ‘is-center’ : ‘’。
效果如下:
使用 HTML 片段
调用方式:
this.$message({
dangerouslyUseHTMLString: true,
message: '<strong>这是 <i>HTML</i> 片段</strong>'
});
组件内添加代码:
<slot>
<p v-if="!dangerouslyUseHTMLString" class="el-message__content">{{ message }}</p>
<p v-else v-html="message" class="el-message__content"></p>
</slot>
v-html即可支持html片段显示。
效果如下:
VNode支持
调用方式:
const h = this.$createElement;
this.$message({
message: h('p', null, [
h('span', null, '内容可以是 '),
h('i', { style: 'color: teal' }, 'VNode')
])
});
在service.js中的Message函数中添加
// 如果是vnode节点,直接将其赋值到instance.$slots.default上即可。
if (isVNode(instance.message)) {
instance.$slots.default = [instance.message];
instance.message = null;
}
再看isVNode方法:
// node是对象,并且有componentOptions属性,我们就认为它是个vnode节点。
function isVNode(node) {
return node !== null && typeof node === 'object' && hasOwn(node, 'componentOptions');
}
我想通过分析vue源码的话,应该可以看到vnode对象拥有componentOptions属性,这里不在细究。
效果如下:
支持多条message显示
下面我们来支持连续出现message的情况。在service.js中:
let instances = [];
const Message = function(options) {
...
// 添加mesagge到dom后
// 当有新message时,它的verticalOffset是前面所有message的高加16
let verticalOffset = options.offset || 20;
instances.forEach(item => {
verticalOffset += item.$el.offsetHeight + 16;
});
instance.verticalOffset = verticalOffset;
instance.visible = true;
instances.push(instance);
return instance;
}
效果如下:
总结
这篇我们研究了Message这个比较有趣的组件。
代码在码云:https://gitee.com/DaBuChen/my-element-ui/tree/message
其他组件源码研究:
Element组件源码研究-Layout,Link,Radio
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 猎户座小陈 原文链接:https://juejin.im/post/6858202395621621767