Element UI 组件源码分析之Alert警告提示

1,900 阅读3分钟

0x00 简介

组件 Alert 用于警告提示,展现需要关注的信息。本文将深入分析源码,剖析其实现原理,耐心读完,相信会对您有所帮助。组件源码实现详见packages/alert/src/main.vue 。 🔗 组件文档 Alert 🔗 gitee源码 main.vue

更多组件分析详见 👉 📚 Element UI 源码剖析组件总览

本专栏的 gitbook 版本地址已经发布 📚《learning element-ui》 ,内容同步更新中!


0x01 组件源码

template 模板内容

组件模板创建一个class名为el-alert<div> 元素根节点 1️⃣,包含两个子节点 :左侧的Icon图标``2️⃣、右侧的文字内容区域 3️⃣。 组件使用内置 transition 实现el-alert-fade 淡入淡出效果。

组件DOM结构渲染如下 👇: image.png

<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 Propertyvisible属性控制组件显示隐藏。组件虽为页面中的非浮层元素不会自动消失,但支持手动关闭隐藏。

  • 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

Icon 不同大小尺寸效果 👇: image.png

3️⃣ 文字内容区域

该节点是一个class 名为el-alert__content<div> 元素,内容包含三个节点: 标题 4️⃣辅助性文字 5️⃣关闭按钮 6️⃣

文字内容区域 DOM 结构渲染如下 👇:

image.png

标题辅助性文字 节点提供了具名插槽和匿名插槽,分发内容时需要指明 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 。

参数说明类型可选值默认值
title标题string
type类型stringsuccess/warning/info/errorinfo
description辅助性文字string
closable是否可关闭booleantrue
center文字是否居中booleantrue
close-text关闭按钮自定义文本string
show-icon是否显示图标booleanfalse
effect选择提供的主题stringlight/darklight

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' : '';
  },
}

Icon 根据组件内容多少(高度也会调整),动态调整尺寸展示更加协调 👇: image.png

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 使用混合指令 bemwhen 嵌套生成组件样式。


//  .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 { /* ...  */ }  

📚参考&关联阅读

"ARIA",MDN

关注专栏

如果本文对您有所帮助请关注➕、 点赞👍、 收藏⭐!您的认可就是对我的最大支持!

此文章已收录到专栏中 👇,可以直接关注。