不推薦通篇閱讀,建議將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