Element-UI阅读理解(3) - 全局命令式组件Message

前言

不推荐通篇阅读,建议将element-ui源码下载并运行到本地,按照本身的方式阅读源码,赶上不明白点能够来这里Ctrl+F(网页内搜索)搜索一下,看这里有没有记录。html

这算不上是分享,只是本身对源码阅读理解的记录,请勿纠结行文杂乱(仍是菜菜,写很差);vue

文章分两部分:node

  • 组件实现的基本原理介绍详细说明
  • 具体分析流程:组件中的大部分的函数、生命周期函数、工具函数/类,做为记录;(不推荐通篇阅读,建议阅读源码时遇到不懂的点,能够来查阅一下)

组件实现的基本原理介绍

  • main.vue肯定了组件的dom结构,设置与class类名的计算函数、关闭事件、定时器、移除dom节点操做;element-ui

  • main.js建立Message实例并设置id字段,用数组instances保存全部Message实例,挂载Message实例并将其添加的<body>上,在Message实例不断增长或删除时,维护各个实例的样式问题(调整上下位置),而且向外暴露了两个操做实例和一种建立实例的api;api

组件Message简直是命令式组件的范本,若是想实现一个相似功能的组件能够看源码参考下,核心是: Vue.extend vm.$mount()数组


具体分析流程(枯燥)

请不要在乎下面的格式和结构,不适合阅读,看源码是思惟有点发散,基本逻辑是看到哪个函数就分析那个函数,顺着函数的上下文顺序和执行顺序走了一遍dom

Message组件代码量较少,分析的顺序大体是:从上到下逐行看代码来理解ide

MessageConstructor

let MessageConstructor = Vue.extend(Main);
// 文档:使用基础 Vue 构造器,建立一个“子类”。参数是一个包含组件选项的对象。
复制代码

main.js 引入的两个工具

import { PopupManager } from 'element-ui/src/utils/popup';
import { isVNode } from 'element-ui/src/utils/vdom';
复制代码

VNode.js

// VNode.js 
import { hasOwn } from 'element-ui/src/utils/util';
export function isVNode(node) return node !== null && typeof node === 'object' && hasOwn(node, 'componentOptions');
};
// hasOwn 是判断属性是不是对象自己的,区别从原型链上继承的属性,从函数的名字上也很好理解(语义化真棒)
复制代码

isVnode的功能就是名字,判断某个东西是不是VNode,组件的参数message可选的类型有两种string / VNode函数

通常地,【工具函数】作判断功能的函数比较简单,判断操做的关联项不会太多,一般是一个或两个关联项,【工具类】而计算操做的函数每每是两个以上的项参与,都抽象成类了;语义化作的好的代码,从名字上能够大体了解工具的做用及复杂度工具

PopupManager

回到PopupManager,首先从名字入手,"弹出窗口管理”,其余弹窗可能也使用了这个工具类,虽然这里只适用了PopupManager.nextZIndex() ,element-ui弹出的组件不少,去其余弹出的组件扫一眼,看下它们对类popup的使用;

扫一眼其余组件对popup的使用状况:

  • 1首先就看Notification通知,和message太像了,果不其然,也只是使用了PopupManager.nextZIndex(),二者的类似度过高了,其余的部分几乎如出一辙,看完Message后不须要看Notification;
  • 2再看MessageBox,名字很像Message长的不像的组件,使用了VNode方法,参数message可选项为string / VNode, 使用了整个popup类,以mixins的形式;

常见两种用法,单独使用PopupManager.nextZIndex()或总体引入popup以mixinx的形式;

main.js这里仅仅使用了PopupManager.nextZIndex()的一个返回zIndex的函数,PopupManager的详细说明会在el-dailog

// element\src\utils\popup\popup-manager.js
const PopupManager = {
···
nextZIndex: function() return PopupManager.zIndex++;},
···
}
// 在对象上添加属性
Object.defineProperty(PopupManager, 'zIndex', {  
    configurable: true,
    get() {  
        if (!hasInitZIndex) {  
            zIndex = zIndex || (Vue.prototype.$ELEMENT || {}).zIndex || 2000;      
            hasInitZIndex = true;    
        }
        return zIndex;  
    }, 
    set(value) {    zIndex = value;  }});
// Vue.prototype.$ELEMENT 在element\src\index.js
复制代码

popup 中集中了弹出组件的通用代码 水忽然深了,暂且无论popup, 对popup 的理解能够结合一个使用了popup的组件(el-dialog)来理解;

Message实例的生成

const Message = fucntion(options) {
···
instance = new MessageConstructor({  data: options});
// 建立 message实例 ,
instances.push(instance);
// 生成 存放message实例的数组instances
}
复制代码
// 刚开始以为这段代码的用途不明啊,没有Message['success'] 啊,
// 看到文档中的示例 this.$message.error('xxx'),才明白
// 初始化时 Message[type]是undefined, 只有这样this.$message.error()调用才会有定义
['success', 'warning', 'info', 'error'].forEach(type => {
    Message[type] = options =>if (typeof options === 'string') { 
            options = { message: options }; 
        } 
        options.type = type; 
        return Message(options);  
    };
});
复制代码

close()相关 如下两行代码好绕啊,名字都挺像的;

// main.vue 
methods: {
    close() {
        this.closed = true// 触发watch closed隐藏消息;
        if (typeof this.onClose === 'function') {  
            this.onClose(this);
        }
    },
}
复制代码

this.onClose(this) 前一个this是实例instance 后一个thisMessage实例,this.onClose(this)执行的是options.onClose

function() { 
        Message.close(id, userOnClose);
    };
复制代码
// main.js
let seed = 1;
const Message = function(options) {
···
    let userOnClose = options.onClose;
    let id = 'message_' + seed++;
    options.onClose = function() { 
        Message.close(id, userOnClose);
    };
    instance = new MessageConstructor({  data: options});
··· 
}
复制代码
// main.js
Message.close = function(id, userOnClose) {...}
// 根据实例id来关闭实例
复制代码

关闭流程中函数的执行时这样的: 点击关闭按钮或this.timer延时器触发 main.vue的close(),执行其中的this.onClose(this);Message.onClose(id, userOnClose),参数id 是Message实例的,userOnClose是在调用时传递的参数;

Message.close = function(id, userOnClose) {
    // 循环instances找到当关闭的instance
    // 有参数onClose的就执行一下useOnClose(instance[i])
    // 把当前instance之下的实例位置都调整一下;
}
复制代码

Message.closeAll() 顾名思义一次关闭全部实例,有须要能够手动调用;

关于延时函数clearTimer/starTimer: 组件监听了鼠标移入移除两个事件,方便鼠标悬停看消息;

@mouseenter="clearTimer"
@mouseleave="startTimer"
复制代码

组件监听了键盘事件,并处理而来Esc 键;

mounted() {  
    this.startTimer(); 
    document.addEventListener('keydown', this.keydown);
},
beforeDestroy() { 
    document.removeEventListener('keydown', this.keydown);
}
// 在组件加载时注册事件监听函数,在组件卸载时除去监听,

keydown(e) { 
    if (e.keyCode === 27) {
        if (!this.closed) { 
            this.close(); 
        } 
    }
}
// 按下esc关闭全部消息  
// 全部实例this.closed值为false, 都会执行this.close;
复制代码

实例注销和dom的移除发生在动画结束时

<transition name="el-message-fade" @after-leave="handleAfterLeave">

handleAfterLeave() { 
    this.$destroy(true);   
    this.$el.parentNode.removeChild(this.$el);
},
复制代码

vue内置的动画组件; 以上就是组件Message基本实现;

Message 消息提示是命令式组件,经常使用于主动操做后的反馈提示。Element 为 Vue.prototype 添加了全局方法 $message。

也能够单独使用

import { Message } from 'element-ui';
复制代码

文档: 此时调用方法为 Message(options)。咱们也为每一个 type 定义了各自的方法,如 Message.success(options)。而且能够调用 Message.closeAll() 手动关闭全部实例。


To Be Contuine

有错误的地方,跪求大佬指点一二

上一个是:el-form el-form-item

下一个是弹窗管理工具类Popup

扩展阅读:

Vue.extend

vm.$mount()

键盘按钮keyCode

相关文章
相关标签/搜索