1. 程式人生 > >element-ui原始碼之元件通訊那些事

element-ui原始碼之元件通訊那些事

  最近在用element-ui重構前端專案,無意之中翻閱到一個比較好用的元件間通訊方式,藉助於vue的封裝的釋出-訂閱訊息模式與mixin語法。在開始之前先總結下vue常用的元件間通訊方式,具體如下:

  1、props與自定義事件

    優點:常用的父子、子父元件傳遞方式,簡單易懂

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

  2、vue 2.4中新增的$attrs與$listeners

    優點:解決了元件巢狀層次較深問題,通過在元件中繫結元件的屬性值與監聽元件的事件監聽物件,子元件可以輕鬆訪問到上層作用域的事件回撥方法

    缺點:相對於props與自定義事件,只是簡化了寫法,本質上屬性也是一級級傳遞的,如果元件的層級是A>B>C,C元件如果想要訪問到A元件的事件回撥,那麼在B元件中需要繫結A元件的屬性attrs與事件物件listeners。

  3、藉助於釋出-訂閱模式

    優點:釋出-訂閱設計模式所擅長點就是解決模組間通訊,無論何種方式的通訊方式,理論上都可以解決,在vue中,大量使用了該設計模式,只需要例項化一個vue例項,作為一個單獨的事件中心引入即可。釋出訂閱模式具體內容可參考:https://www.cnblogs.com/gerry2019/p/10241488.html

    缺點:釋出-訂閱模式只要有訂閱訊息,在分發事件的時候,都會觸發訂閱的回撥,所以在開發過程中要注意自定義事件的名稱空間,防止一些不必要的觸發操作。

  4、藉助vuex管理狀態

    優點:引入狀態管理,可以統一的管理專案狀態,針對大型複雜專案適用性較好

    缺點:需要額外維護一個vuex物件,中小型專案不適合

  5、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-in