0x00 简介
组件 Alert
用于警告提示,展现需要关注的信息。本文将深入分析源码,剖析其实现原理,耐心读完,相信会对您有所帮助。组件源码实现详见packages/alert/src/main.vue
。 🔗 组件文档 Alert 🔗 gitee源码 main.vue
更多组件剖析详见 👉 📚 Element 2 源码剖析组件总览 。
0x01 组件源码
template 模板内容
组件模板创建一个class名为el-alert
的<div>
元素根节点 1️⃣
,包含两个子节点 :左侧的Icon图标``2️⃣
、右侧的文字内容区域
3️⃣
。 组件使用内置 transition
实现el-alert-fade
淡入淡出效果。
组件DOM结构渲染如下 👇:
<template>
<transition name="el-alert-fade">
<div
class="el-alert"
:class="[typeClass, center ? 'is-center' : '', 'is-' + effect]"
v-show="visible"
role="alert"
>
<!-- icon 图标 -->
<i class="el-alert__icon" :class="[ iconClass, isBigIcon ]" v-if="showIcon"></i>
<!-- 文字内容 包含关闭按钮 -->
<div class="el-alert__content">
<!-- 标题 -->
<span class="el-alert__title" :class="[ isBoldTitle ]" v-if="title || $slots.title">
<slot name="title">{{ title }}</slot>
</span>
<!-- 辅助性文字介绍 -->
<p class="el-alert__description" v-if="$slots.default && !description"><slot></slot></p>
<p class="el-alert__description" v-if="description && !$slots.default">{{ description }}</p>
<!-- 关闭按钮 -->
<i class="el-alert__closebtn" :class="{ 'is-customed': closeText !== '',
'el-icon-close': closeText === '' }" v-show="closable" @click="close()">
{{closeText}}
</i>
</div>
</div>
</transition>
</template>
1️⃣ 根节点
根据传入prop的属性值动态添加class。
计算属性typeClass
根据 type
属性值生成不同类型样式 class , el-alert--[success/warning/info/error]
。
根据 center
属性值生成is-center
,让图标文字水平居中。
根据 effect
属性值生成主题样式 is-[light/dark]
。
v-show="visible"
使用 Data Property 的 visible
属性控制组件显示隐藏。组件虽为页面中的非浮层元素不会自动消失,但支持手动关闭隐藏。
role="alert"
表示当前元素的类型,用于 ARIA
无障碍访问设置。
根节点元素内部使用flex布局。两个子节点交叉轴的中点对齐。
.el-alert {
display: flex;
/* 交叉轴的中点对齐 */
align-items: center;
}
is-center
通过设定属性justify-content
定义了主轴上的对齐方式,实现两个子节点水平居中。
.is-center {
justify-content: center;
}
2️⃣ Icon图标
该节点是一个Icon 图标元素,表示某种状态时提升可读性,图标节点是否渲染可见根据 showIcon
属性值决定 v-if="showIcon"
。
静态样式 el-alert__icon
定义图标默认尺寸。
使用计算属性 iconClass
isBigIcon
动态添加样式。
计算属性iconClass
根据 type
属性值生成不同类型 icon class , el-icon-[success/warning/info/error]
。
计算属性isBigIcon
根据 辅助性文字内容是否设置,生成图标大尺寸样式is-big
。
3️⃣ 文字内容区域
该节点是一个class 名为el-alert__content
的<div>
元素,内容包含三个节点: 标题
4️⃣
、 辅助性文字
5️⃣
、 关闭按钮
6️⃣
。
文字内容区域 DOM 结构渲染如下 👇:
标题
、 辅助性文字
节点提供了具名插槽和匿名插槽,分发内容时需要指明 slot#name
,否则内容都会分发给匿名插槽。
4️⃣ 标题
该节点是一个class 名为el-alert__title
的<span>
元素,包裹着具名插槽 title
。
只有在 title
属性值是 truthy
或者向该插槽分发内容时,该节点才会被渲染。title
是插槽的后备内容
。若两者都设置了(传入title
值而且向该插槽提供内容),渲染展示内容为插槽分发内容。
使用计算属性 isBoldTitle
动态添加样式。 isBoldTitle
根据辅助性文字内容是否设置,生成样式is-big
加粗title字体,用于区分与辅助性文字的主次。
.is-bold {
font-weight: 700;
}
5️⃣ 辅助性文字
该节点是一个class 名为el-alert__description
的<p>
段落元素。该节点提供了匿名插槽或 description
属性值,但是两者只能同时设置一项,才能正常显示。
当然可以改成如下逻辑,默认显示description
属性值,插槽分发内容后优先显示后者。
<p class="el-alert__description" v-if="description || $slots.default">
<slot> {{ description }} </slot>
</p>
基于很多组件的逻辑实现: prop属性大多作为插槽的后备内容, 暂时还不知为啥提供这么对立条件,有他无我!
6️⃣ 关闭按钮
节点默认是一个名为el-icon-close
的Icon 图标,提供close
事件来设置关闭时的回调。通过 closable
属性决定是否可关闭。
若设置closeText
属性值自定义关闭按钮,此时节点为包裹文本的 <i>
元素。
关闭按钮位置偏移至右上角,样式el-alert__closebtn
定义如下:
.el-alert__closebtn {
position: absolute;
top: 12px;
right: 15px;
}
attributes 属性
组件定义了8个prop 。
effect
提供了自定义验证函数,提供了默认值light
。 若传入effect
值不是以下light/dark
其中之一,会生成无效的样式is-[effect]
,导致组件样式无法正常加载。
props: {
effect: {
type: String,
default: 'light',
validator: function(value) {
return ['light', 'dark'].indexOf(value) !== -1;
}
}
},
计算属性
typeClass
根据 type
属性值生成不同类型主题样式(背景颜色、文字颜色) el-alert-[success/warning/info/error]
。
computed: {
typeClass() {
return `el-alert--${ this.type }`;
},
}
iconClass
计算属性iconClass
根据 type
属性值生成不同类型 icon class el-icon-[success/warning/info/error]
。
const TYPE_CLASSES_MAP = {
'success': 'el-icon-success',
'warning': 'el-icon-warning',
'error': 'el-icon-error'
};
computed: {
iconClass() {
return TYPE_CLASSES_MAP[this.type] || 'el-icon-info';
},
}
若传入type
值不是以下success/warning/error
其中之一,都会生成el-icon-info
,所以type
默认值等于info
。
isBigIcon
根据辅助性文字内容是否设置,只有在 title
属性值是 truthy
或者向该插槽分发内容时,生成样式is-big
加粗title字体,用于区分与辅助性文字的主次。
computed: {
isBigIcon() {
return this.description || this.$slots.default ? 'is-big' : '';
},
}
isBoldTitle
根据辅助性文字内容是否设置,只有在 title
属性值是 truthy
或者向该插槽分发内容时,生成图标大尺寸样式is-big
computed: {
isBoldTitle() {
return this.description || this.$slots.default ? 'is-bold' : '';
}
}
close 事件
组件监听按钮的 click
事件 。当点击关闭按钮时,触发 click
事件,执行 close()
方法,设置visible
属性值 false
,隐藏组件;调用 this.$emit('close')
触发当前实例上的close
事件 。
close() {
// 隐藏组件
this.visible = false;
// 触发当前实例上的事件
this.$emit('close');
}
0x02 组件样式
src/alert.scss
组件样式源码 packages\theme-chalk\src\alert.scss
使用混合指令 b
、e
、m
、when
嵌套生成组件样式。
// .el-alert
@include b(alert) {
// ...
@include when(light) {
// .el-alert.is-light .el-alert__closebtn
.el-alert__closebtn { /* ... */ }
}
@include when(dark) {
// .el-alert.is-dark .el-alert__closebtn
.el-alert__closebtn { /* ... */ }
// .el-alert.is-dark .el-alert__description
.el-alert__description { /* ... */ }
}
// .el-alert.is-center
@include when(center) { /* ... */ }
// success warning error 格式类似
@include m(success) {
// .el-alert--success.is-light
&.is-light {
// ...
//.el-alert--success.is-light .el-alert__description
.el-alert__description { /* ... */ }
}
// .el-alert--success.is-dark
&.is-dark { /* ... */ }
}
// info
@include m(info) {
//.el-alert--info.is-light
&.is-light { /* ... */ }
// .el-alert--info.is-dark
&.is-dark { /* ... */ }
// .el-alert--info .el-alert__description
.el-alert__description { /* ... */ }
}
// .el-alert__content
@include e(content) { /* ... */ }
// .el-alert__icon
@include e(icon) {
// ...
// .el-alert__icon.is-big
@include when(big) { /* ... */ }
}
// .el-alert__title
@include e(title) {
// ...
// .el-alert__title.is-bold
@include when(bold) { /* ... */ }
}
// .el-alert .el-alert__description
& .el-alert__description { /* ... */ }
// .el-alert__closebtn
@include e(closebtn) {
// ...
// .el-alert__closebtn.is-customed
@include when(customed) { /* ... */ }
}
}
.el-alert-fade-enter, .el-alert-fade-leave-active { /* ... */ }
lib/alert.css
前文可知使用 gulpfile.js
编译 scss
文件转换为CSS
,经过浏览器兼容、格式压缩,最后生成 packages\theme-chalk\lib\alert.scss
,内容格式如下。
.el-alert { /* ... */ }
.el-alert.is-light .el-alert__closebtn { /* ... */ }
.el-alert.is-dark .el-alert__closebtn,
.el-alert.is-dark .el-alert__description { /* ... */ }
.el-alert.is-center { /* ... */ }
/* ...success... */
.el-alert--success.is-light { /* ... */ }
.el-alert--success.is-light .el-alert__description { /* ... */ }
.el-alert--success.is-dark { /* ... */ }
/* ...warning... */
/* ...error... */
/* ...info... */
.el-alert--info.is-light { /* ... */ }
.el-alert--info.is-dark { /* ... */ }
.el-alert--info .el-alert__description { /* ... */ }
.el-alert__content { /* ... */ }
.el-alert__icon { /* ... */ }
.el-alert__icon.is-big { /* ... */ }
.el-alert__title { /* ... */ }
.el-alert__title.is-bold { /* ... */ }
.el-alert .el-alert__description { /* ... */ }
.el-alert__closebtn { /* ... */ }
.el-alert__closebtn.is-customed { /* ... */ }
.el-alert-fade-enter, .el-alert-fade-leave-active { /* ... */ }
0x03 📚参考
"ARIA",MDN