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

相關文章
相關標籤/搜索