Element 2 组件源码剖析之Message消息提示
Posted QXXXD
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Element 2 组件源码剖析之Message消息提示相关的知识,希望对你有一定的参考价值。
简介
消息提示组件 Message
常用于主动操作后的反馈提示,顶部居中显示并自动消失,是一种不打断用户操作的轻量级提示方式。本文将分析其源码实现,耐心读完,相信会对您有所帮助。🔗 组件文档 Message 。
使用方式
组件Message
以服务的方式调用。Message
组件入口文件中没有,没有插件声明,只是导出了方法 Message
;在组件库入口文件中,将方法 Message
添加至 Vue.prototype
。
// `Message` 组件入口文件
// packages\\message\\index.js
import Message from './src/main.js';
export default Message;
在组件库入口文件也是一样的处理。
// 组件库入口
// src/index.js
import Message from '../packages/message/index.js';
//...
const install = function(Vue, opts = )
//...
Vue.prototype.$message = Message;
;
export default
//...
Message,
;
调用方法为 Message(options)
。组件也为每个 type 定义了各自的方法,如 Message.success(options)
。调用 Message.closeAll()
手动关闭所有实例。组件库完整引入,直接使用this.$message(options)
。
// 完整引入
this.$message(options);
// 单独引用
import Message from 'element-ui';
// ...
Message(options);
其中 options
参数为 Message
的配置项,在此不做详尽解释,详见 组件文档 Message #options。
组件源码
html
消息提示组件页面元素结构比较简单。
根节点下元素按照功区分,主要有三部分:
- Icon 图标
- 消息文字
- 关闭按钮
使用 transition
组件,在组件根节点的条件展示 (v-show
)中添加过渡效果,定义了钩子函数after-leave
用于设置过渡离开完成之后的组件状态。
// packages\\message\\src\\main.vue
<template>
<!-- transition过渡组件,绑定after-leave钩子 -->
<transition name="el-message-fade" @after-leave="handleAfterLeave">
<!-- 组件根节点 -->
<div
:class="[
'el-message',
type && !iconClass ? `el-message--$ type ` : '',
center ? 'is-center' : '',
showClose ? 'is-closable' : '',
customClass
]"
:style="positionStyle"
v-show="visible" >
<!-- 主题/自定义图标 -->
<i :class="iconClass" v-if="iconClass"></i>
<i :class="typeClass" v-else></i>
<!-- 默认插槽 -->
<slot>
<p v-if="!dangerouslyUseHTMLString" class="el-message__content"> message </p>
<p v-else v-html="message" class="el-message__content"></p>
</slot>
<!-- 关闭图标 -->
<i v-if="showClose" class="el-message__closeBtn el-icon-close" @click="close"></i>
</div>
</transition>
</template>
<script>
// 主题类型图标映射
const typeMap =
success: 'success',
info: 'info',
warning: 'warning',
error: 'error'
;
export default
data()
return
visible: false, // 组件显示状态
message: '', // 消息文字
duration: 3000, // 显示时间, 毫秒
type: 'info', // 状态主题
iconClass: '', // 自定义图标的类名
customClass: '', // 自定义类名
onClose: null, // 关闭时的回调函数
showClose: false, // 是否显示关闭按钮
closed: false, // 组件关闭状态
verticalOffset: 20, // 距离顶部的偏移 top: 20px
timer: null, // 定时器,控制组件自动关闭
dangerouslyUseHTMLString: false, // 是否将 message 属性作为 HTML 片段处理
center: false // 文字是否居中
;
,
computed:
// 不同主题type的图标
typeClass()
return this.type && !this.iconClass
? `el-message__icon el-icon-$ typeMap[this.type] `
: '';
,
// 设置top
positionStyle()
return
'top': `$ this.verticalOffset px`
;
,
// ...
;
</script>
top 偏移量
元素根节点是一个类名el-message
的div元素,使用绝对布局。fixed
表示脱离文档流,通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置,水平方向居中,垂直方向居上。
.el-message
position: fixed;
left: 50%;
top: 20px;
使用计算属性 positionStyle
基于设置的verticalOffset
属性值动态计算组件距离顶部的偏移量。
// top: 20px
positionStyle()
return
'top': `$ this.verticalOffset px`
;
页面中可以存在多个Message
实例,新 Message 消息会在旧的下面展示,此时实例根据在数组中所处索引值,计算出实例的距离顶部的偏移量 verticalOffset
。组件实例随着自动/人工关闭销毁,数组内容变量,其索引值会变化,verticalOffset
值也会重新计算,下文“服务实现”一节中会详细介绍该逻辑。
状态主题
状态主题属性type
默认值 info
, 组件支持success/warning/info/error
共四种可选值。
根节点中基于type
值生成不同主题样式el-message--[success/warning/info/error]
。但当传入属性iconClass
值用于自定义图标的类名,就不会生成主题样式,此时 type
设置无效。
type && !iconClass ? `el-message--$ type ` : '',
子元素内容布局
message
组件内部使用flex布局。属性 center
用于生成类名is-center
设置图标和消息文字居中。
.el-message
display: flex;
align-items: center;
.el-message.is-center
justify-content: center;
关闭图标使用绝对布局,垂直居中水平居右。
<!-- 关闭图标 -->
<i v-if="showClose" class="el-message__closeBtn el-icon-close" @click="close"></i>
.el-message__closeBtn
position: absolute;
top: 50%;
right: 15px;
当时显示关闭图标时,会生成类名is-closable
,防止消息文字跟关闭按钮由重叠。
.el-message.is-closable .el-message__content
padding-right: 16px;
Icon图标优先显示自定义。主题图标的类名使用计算属性typeClass
。
<!-- 主题/自定义图标 -->
<i :class="iconClass" v-if="iconClass"></i>
<i :class="typeClass" v-else></i>
消息文字插槽
属性 message
支持传入 HTML 片段,但是需要显示打开此功能(将属性dangerouslyUseHTMLString
设置 true)。
<!-- 默认插槽 -->
<slot>
<p v-if="!dangerouslyUseHTMLString" class="el-message__content"> message </p>
<p v-else v-html="message" class="el-message__content"></p>
</slot>
当属性 message
传入值类型为VNode
时,会使用插槽功能,下文“服务实现”一节会详细介绍。
// packages\\message\\src\\main.js
if (isVNode(instance.message))
instance.$slots.default = [instance.message];
instance.message = null;
在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。因此在
dangerouslyUseHTMLString
打开的情况下,请确保message
的内容是可信的,永远不要将用户提交的内容赋值给message
属性。
生命周期 & 事件
组件被挂载之后调用方法startTimer
启用定时器,实现 message 实例的自动关闭。在方法startTimer
中当属性duration
值大于0时(if (this.duration > 0)
),才会创建定时器用于自动关闭;若组件不需要自动关闭,将属性duration
值设置为 0
即可。
挂载之后添加keydown
事件监听。实例销毁之前,会移除keydown
事件监听。方法 keydown
用于实现按ESC
键关闭消息组件。如果页面存在多个实例,会全部关闭。
使用自定义侦听器,当属性closed
值变化且为 true 时,将属性visible
值置为false(组件隐藏)。
export default
// ...
// 挂载时
mounted()
// 启动定时器
this.startTimer();
// 监听keydown事件
document.addEventListener('keydown', this.keydown);
,
beforeDestroy()
// 取消keydown监听
document.removeEventListener('keydown', this.keydown);
,
watch:
closed(newVal)
if (newVal)
this.visible = false;
,
methods:
// 组件关闭事件
close()
// ...
,
// 过渡`after-leave`钩子函数
handleAfterLeave()
// ...
,
// 清除定时器,当mouseenter时调用
clearTimer()
clearTimeout(this.timer);
,
// 启动定时器,duration默认是3s,到时间自动隐藏
startTimer()
if (this.duration > 0)
this.timer = setTimeout(() =>
if (!this.closed)
this.close();
, this.duration);
,
// 监听键盘按键事件
keydown(e)
if (e.keyCode === 27) // esc关闭消息
if (!this.closed)
this.close();
,
;
根节点绑定 mouseenter
、mouseleave
事件,当鼠标移动到 message 实例上,会清除其定时器clearTimer
,该实例就不会自动关闭。当鼠标移出后,会重新创建定时器startTimer
,实现自动关闭。
<div @mouseenter="clearTimer" @mouseleave="startTimer" >
// ...
</div>
方法close
用于关闭组件,如果用户设置了属性onClose
值,关闭时会执行该回调函数, 参数为被关闭的 message 实例。
close()
this.closed = true;
if (typeof this.onClose === 'function')
this.onClose(this);
,
当组件关闭后,会触发transition
组件绑定after-leave
钩子函数,执行方法 handleAfterLeave
。过渡离开完成之后,执行方法vm.$destroy()
,完全销毁该实例,触发 beforeDestroy
的钩子;同时将实例 DOM 元素从页面移除
handleAfterLeave()
// 完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器
// 触发 beforeDestroy 和 destroyed 的钩子
this.$destroy(true);
this.$el.parentNode.removeChild(this.$el);
,
以上是关于Element 2 组件源码剖析之Message消息提示的主要内容,如果未能解决你的问题,请参考以下文章