不推荐通篇阅读,建议将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
let MessageConstructor = Vue.extend(Main);
// 文档:使用基础 Vue 构造器,建立一个“子类”。参数是一个包含组件选项的对象。
复制代码
import { PopupManager } from 'element-ui/src/utils/popup';
import { isVNode } from 'element-ui/src/utils/vdom';
复制代码
// 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.nextZIndex()
,element-ui弹出的组件不少,去其余弹出的组件扫一眼,看下它们对类popup
的使用;
扫一眼其余组件对popup的使用状况:
Notification
通知,和message太像了,果不其然,也只是使用了PopupManager.nextZIndex()
,二者的类似度过高了,其余的部分几乎如出一辙,看完Message后不须要看Notification;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
)来理解;
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
后一个this
是Message
实例,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