• 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; 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在组件库入口文件也是一样的处理。

    // 组件库入口 
    // src/index.js
    import Message from '../packages/message/index.js';
    //... 
    const install = function(Vue, opts = {}) {
      //...
      Vue.prototype.$message = Message; 
    
    }; 
    export default {
      //...
      Message,
    }; 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    调用方法为 Message(options)。组件也为每个 type 定义了各自的方法,如 Message.success(options)。调用 Message.closeAll() 手动关闭所有实例。组件库完整引入,直接使用this.$message(options)

    // 完整引入
    this.$message(options);
    
    // 单独引用
    import { Message } from 'element-ui';
    // ...
    Message(options);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    其中 options 参数为 Message 的配置项,在此不做详尽解释,详见 组件文档 Message #options

    组件源码

    HTML

    消息提示组件页面元素结构比较简单。

    根节点下元素按照功区分,主要有三部分:

    • Icon 图标
    • 消息文字
    • 关闭按钮

    使用 transition 组件,在组件根节点的条件展示 (v-show)中添加过渡效果,定义了钩子函数after-leave 用于设置过渡离开完成之后的组件状态。

    // packages\message\src\main.vue
     
     
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    top 偏移量

    元素根节点是一个类名el-message的div元素,使用绝对布局。fixed表示脱离文档流,通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置,水平方向居中,垂直方向居上。

    .el-message { 
      position: fixed;
      left: 50%;
      top: 20px; 
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用计算属性 positionStyle 基于设置的verticalOffset属性值动态计算组件距离顶部的偏移量。

    // top: 20px
    positionStyle() {
      return {
        'top': `${ this.verticalOffset }px`
      };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    页面中可以存在多个Message 实例,新 Message 消息会在旧的下面展示,此时实例根据在数组中所处索引值,计算出实例的距离顶部的偏移量 verticalOffset。组件实例随着自动/人工关闭销毁,数组内容变量,其索引值会变化,verticalOffset值也会重新计算,下文“服务实现”一节中会详细介绍该逻辑。

    状态主题

    状态主题属性type默认值 info, 组件支持success/warning/info/error共四种可选值。

    根节点中基于type值生成不同主题样式el-message--[success/warning/info/error]。但当传入属性iconClass值用于自定义图标的类名,就不会生成主题样式,此时 type设置无效。

    type && !iconClass ? `el-message--${ type }` : '',
    
    
    • 1
    • 2

    子元素内容布局

    message 组件内部使用flex布局。属性 center用于生成类名is-center设置图标和消息文字居中。

    .el-message { 
      display: flex; 
      align-items: center;
    } 
    .el-message.is-center { 
      justify-content: center;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    关闭图标使用绝对布局,垂直居中水平居右。

    
    
          
    .el-message__closeBtn {
      position: absolute;
      top: 50%;
      right: 15px; 
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当时显示关闭图标时,会生成类名is-closable ,防止消息文字跟关闭按钮由重叠。

    .el-message.is-closable .el-message__content {
      padding-right: 16px;
    }
    
    
    • 1
    • 2
    • 3
    • 4

    Icon图标优先显示自定义。主题图标的类名使用计算属性typeClass

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4

    消息文字插槽

    属性 message 支持传入 HTML 片段,但是需要显示打开此功能(将属性dangerouslyUseHTMLString 设置 true)。

    
    
      

    {{ message }}

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    当属性 message 传入值类型为VNode时,会使用插槽功能,下文“服务实现”一节会详细介绍。

    // packages\message\src\main.js
    if (isVNode(instance.message)) {
        instance.$slots.default = [instance.message];
        instance.message = null;
      }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在网站上动态渲染任意 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();
            }
          }
        }
      },
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    根节点绑定 mouseentermouseleave 事件,当鼠标移动到 message 实例上,会清除其定时器clearTimer,该实例就不会自动关闭。当鼠标移出后,会重新创建定时器startTimer,实现自动关闭。

    // ...
    • 1
    • 2
    • 3
    • 4

    方法close用于关闭组件,如果用户设置了属性onClose值,关闭时会执行该回调函数, 参数为被关闭的 message 实例。

    close() {
      this.closed = true;
      if (typeof this.onClose === 'function') {
        this.onClose(this);
      }
    },
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    当组件关闭后,会触发transition 组件绑定after-leave钩子函数,执行方法 handleAfterLeave。过渡离开完成之后,执行方法vm.$destroy(),完全销毁该实例,触发 beforeDestroy 的钩子;同时将实例 DOM 元素从页面移除

    handleAfterLeave() {
      // 完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器
      // 触发 beforeDestroy 和 destroyed 的钩子
      this.$destroy(true);
      this.$el.parentNode.removeChild(this.$el);
    },
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    【MAPBOX基础功能】15、mapbox地图事件:点击、移入、移出、解绑
    Android平台实现系统内录(捕获播放的音频)并推送RTMP服务技术方案探究
    常用的英文缩写总结,专业术语
    c++实现图书管理系统v1.0
    JAVA毕业设计105—基于Java+Springboot+Vue的校园跑腿系统(源码+数据库)
    JVM -运行时数据区 - 堆空间
    logstash连接elasticsearch https
    Mysql 零宽空格ZWSP,导致表面同一段一摸一样的sql无法查询出相同结果
    Visual Studio App Center 中的 Bug 跟踪服务
    SpringBoot Servlet容器启动解析
  • 原文地址:https://blog.csdn.net/QXXXD/article/details/126267639