element-ui源碼之組件通訊那些事

  最近在用element-ui重構前端項目,無心之中翻閱到一個比較好用的組件間通訊方式,藉助於vue的封裝的發佈-訂閱消息模式mixin語法。在開始以前先總結下vue經常使用的組件間通訊方式,具體以下:html

  一、props與自定義事件前端

    優勢:經常使用的父子、子父組件傳遞方式,簡單易懂vue

    缺點:子父、父子之間傳參比較高效,可是爺孫,兄弟組件之間存在通訊短板,只能一級級傳遞vuex

  二、vue 2.4中新增的$attrs與$listenerselement-ui

    優勢:解決了組件嵌套層次較深問題,經過在組件中綁定組件的屬性值與監聽組件的事件監聽對象,子組件能夠輕鬆訪問到上層做用域的事件回調方法設計模式

    缺點:相對於props與自定義事件,只是簡化了寫法,本質上屬性也是一級級傳遞的,若是組件的層級是A>B>C,C組件若是想要訪問到A組件的事件回調,那麼在B組件中須要綁定A組件的屬性attrs與事件對象listeners。api

  三、藉助於發佈-訂閱模式app

    優勢:發佈-訂閱設計模式所擅長點就是解決模塊間通訊,不管何種方式的通訊方式,理論上均可以解決,在vue中,大量使用了該設計模式,只須要實例化一個vue實例,做爲一個單獨的事件中心引入便可。發佈訂閱模式具體內容可參考:http://www.javashuo.com/article/p-ctyewxps-dv.htmlide

    缺點:發佈-訂閱模式只要有訂閱消息,在分發事件的時候,都會觸發訂閱的回調,因此在開發過程當中要注意自定義事件的命名空間,防止一些沒必要要的觸發操做。ui

  四、藉助vuex管理狀態

    優勢:引入狀態管理,能夠統一的管理項目狀態,針對大型複雜項目適用性較好

    缺點:須要額外維護一個vuex對象,中小型項目不適合

  五、vue 2.2中新增的provide/inject

    優勢:能夠從父組件注入須要共享給子元素的數據,若是注入的的數據是響應式,那麼組件之間能夠實現雙向數據通訊

    缺點:官方並不推薦在業務代碼中使用此模式,由於這種雙向通訊可能會致使組件的狀態發生混亂

  以上爲vue中經常使用的幾種組件間通訊方式,如下爲element-ui中採用的組件通訊方式,本質上也是發佈-訂閱模式,與上述方法3大體類似,只是引入了自定義事件的生效的做用域,能夠規避一些意料以外的事件觸發。由於稍微不注意消息的命名空間,就可能會致使一些意外以外的錯誤,除此以外,當兩個模塊功能類似,若是單獨都寫一遍,可能存在大量冗餘代碼,在vue中咱們想到的策略多是藉助於mixin,抽離公共的業務代碼,可是假如抽離出了公共的消息訂閱內容,那麼此時問題就來了,若是我只想A模塊內容發生改變,B模塊不變,這個就會存在策略性問題,element-ui中的通訊策略能夠很好的規避掉以上兩種坑,具體實現邏輯以下:

/**
 * 
 * @param {目標組件名稱} componentName 
 * @param {事件名稱} eventName 
 * @param {載荷參數} params 
 */
function broadcast(componentName, eventName, params) {
    //遞歸子組件,查找命名空間內組件
    this.$children.forEach(child => {
        var name = child.$options.componentName;

        if (name === componentName) {
            //分發子組件內訂閱消息
            child.$emit.apply(child, [eventName].concat(params));
        } else {
            broadcast.apply(child, [componentName, eventName].concat([params]));
        }
    });
}
export default {
    methods: {
        /**
         * 
         * @param {目標組件名稱} componentName 
         * @param {事件名稱} eventName 
         * @param {載荷參數} params 
         */
        dispatch(componentName, eventName, params) {
            var parent = this.$parent || this.$root;
            var name = parent.$options.componentName;
            //循環查詢父組件,找到目標父組件
            while (parent && (!name || name !== componentName)) {
                parent = parent.$parent;

                if (parent) {
                    name = parent.$options.componentName;
                }
            }
            if (parent) {
                //分發父組件訂閱內容
                parent.$emit.apply(parent, [eventName].concat(params));
            }
        },
        broadcast(componentName, eventName, params) {
            broadcast.call(this, componentName, eventName, params);
        }
    }
};

  從以上代碼能夠看出,在組件通訊過程當中,須要額外加一個componetName屬性,其主要做用就是對分發的事件加入一個命名空間命名空間內部的事件纔會觸發,不然會直接跳過,增長了業務代碼的容錯能力。在使用的過程當中,只須要在須要引用的地方引入這個混入,就能夠實現組件間雙向通訊。element-ui中的具體使用以下:

  

  mixin語法可參考:https://cn.vuejs.org/v2/guide/mixins.html

  mixin本質上就相似於繼承,有助於簡化業務代碼

  vue 2.x中部分新增組件通訊方式可參閱vue API文檔:https://cn.vuejs.org/v2/api/#provide-inject

相關文章
相關標籤/搜索