亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

Vue中的情侶屬性$dispatch和$broadcast詳解

2019-11-19 12:01:44
字體:
來源:轉載
供稿:網友

00 前言

$dispatch 和 $broadcast 作為一對情侶 💑屬性,在 Vue 1.0 中主要用來實現基于組件樹結構的事件流通信 ―― 通過向上或向下以冒泡的形式傳遞事件流,以實現嵌套父子組件的通信。但是由于其顯功能缺陷,在 Vue 2.0 中就被移除了。雖然 Vue 官網已經不再支持使用 $dispatch 和 $broadcast 進行組件通信,但是在很多基于 Vue 的 UI 框架中都有對其的封裝,包括 element-ui、iview 等等。

那么 $dispatch 和 $broadcast 到底是怎么工作,其底層又是怎么實現的呢?接下來,我們就詳細的說一說!

01 $dispatch 詳解

為了追根溯源,我們還是先去 Vue 1.0 的文檔你觀摩一下其概念吧!

概念:

Dispatch an event, first triggering it on the instance itself, and then propagates upward along the parent chain. The propagation stops when it triggers a parent event listener, unless that listener returns true. Any additional arguments will be passed into the listener's callback function.

上面的一段英文定義來自 Vue 1.0 官方文檔,其大致的意思是說:dispatch 是一個事件,首先會在自己實例本身上觸發,然后沿父鏈向上傳播。當它觸發父組件上的事件偵聽器時傳播即會停止,除非該偵聽器返回 true。 任何其他參數都將傳遞給偵聽器的回調函數。

參數:

dispatch 會接收兩中參數:event 是事件名稱,[...args] 是觸發事件時傳遞給回調函數的參數。

**例子:

// 創建一個 parent 組件var parent = new Vue();// 創建一個 child1 組件,其父組件指向 parentvar child1 = new Vue({ parent: parent });// 創建一個 child2 組件,其父組件指向 child1var child2 = new Vue({ parent: child1 });// 在 parent 組件監聽名為 test 的事件,并綁定了一個回調函數parent.$on('test', function () { console.log('parent notified');});// 在 child1 組件監聽名為 test 的事件,并綁定了一個回調函數child1.$on('test', function () { console.log('child1 notified');});// 在 child2 組件監聽名為 test 的事件,并綁定了一個回調函數child2.$on('test', function () { console.log('child2 notified');});

說到這里,parent、child1 和 child2 三個組件之間的關系可以展示成如下的關系圖:

// 在 child2 組件中通過 dispatch 觸發 test 事件child2.$dispatch('test');// 事件執行會輸出如下結果// -> "child2 notified"http:// -> "child1 notified"

當執行 child2.$dispatch('test'); 時,首先會觸發 child2 組件里面監聽的 test 事件的回調函數,輸出 'child2 notified',根據上面官方文檔的定義,事件會沿著組件關系鏈一直向上傳遞,然后傳遞到 child1 組件,觸發監聽事件輸出 "child1 notified",但是該偵聽器沒有返回 true,所以事件傳遞到此就結束了,最終的輸出結果就只有 "child2 notified" 和 "child1 notified"。

Vue 1.0 官方實現

在 Vue 1.0 版本中,$dispatch 實現的源碼放在 /src/instance/api/events.js 文件中,代碼很簡單:

/** * Recursively propagate an event up the parent chain. * 遞歸地在父鏈上傳播事件。 * @param {String} event * @param {...*} additional arguments */// $dispatch 方法是定義在 Vue 的 prototype 上的// 接受一個字符串類型的事件名稱Vue.prototype.$dispatch = function (event) { // 首先執行 $emit 觸發事件,將返回值保存在 shouldPropagate 中 var shouldPropagate = this.$emit.apply(this, arguments)  // 如果首次執行的 $emit 方法返回的值不是 true 就直接返回 // 如果返回值不是 true 就說明組件邏輯不希望事件繼續往父組件進行傳遞 if (!shouldPropagate) return  // 如果首次執行 $emit 方法返回值是 true 就獲取當前組件的 parent 組件實例 var parent = this.$parent  // 將函數接受的參數轉換成數組 var args = toArray(arguments)  // use object event to indicate non-source emit on parents // 根據傳入的事件名稱的參數組裝成 object args[0] = { name: event, source: this }  // 循環知道組件的父組件 while (parent) { // 在父組件中執行 $emit 觸發事件 shouldPropagate = parent.$emit.apply(parent, args)  // 如果父組件 $emit 返回的是 true 就繼續遞歸祖父組件,否則就停止循環 parent = shouldPropagate ? parent.$parent : null }  // 最后返回當前組件實例 return this}

element-ui 實現

在 element-ui 中,$dispatch 實現的源碼放在 /src/mixins/emitter.js 文件中,代碼很簡單:

// 定義 dispatch 方法,接受三個參數,分別是:組件名稱、將要觸發的事件名稱、回調函數傳遞的參數dispatch(componentName, eventName, params) { // 獲取基于當前組件的父組件實例,這里對父組件實例和根組件實例做了兼容處理 var parent = this.$parent || this.$root;  // 通過父組件的 $option 屬性獲取組件的名稱 var name = parent.$options.componentName; // 當相對當前組件的父組件實例存在,而且當父組件的名稱不存在或者父組件的名稱不等于傳入的組件名稱時,執行循環 while (parent && (!name || name !== componentName)) { // 記錄父組件的父組件 parent = parent.$parent; // 當父組件的父組件存在時,獲取祖父組件的名稱 if (parent) {  name = parent.$options.componentName; } }  // 當循環結束是,parent 的值就是最終匹配的組件實例 if (parent) { // 當 parent 值存在時調用 $emit 方法 // 傳入 parent 實例、事件名稱與 params 參數組成的數組 // 觸發傳入事件名稱 eventName 同名的事件 parent.$emit.apply(parent, [eventName].concat(params)); }}

差異分析

仔細看完實現 $dispatch 方式的兩個版本的代碼,大家是不是發現,兩個版本的實現和功能差異性還是很大的。

1、接受參數:Vue 實現版本只會接受一個字符串類型的事件名稱為參數,而 element-ui 實現的版本會接受三個參數,分別是:需要觸發事件的組件名稱、將要觸發的事件名稱、回調函數傳遞的參數;

2、實現功能:Vue 實現版本觸發事件一直會順著組件鏈向上進行傳遞,知道父組件中的偵聽器沒有返回 true,在這個期間所有的組件都會執行事件的響應,包括當前組件本身,而 element-ui 實現版本會不斷的基于當前組件向父組件進行遍歷,直至找到和接受的組件名稱匹配,就會停止遍歷,觸發匹配組件中的監聽事件。

10 $broadcast 詳解

上面詳細的說完 $dispatch 方法的實現和 Vue 實現版本與 element-ui 實現版本的區別,下面就該說說 $broadcast,畢竟他們是情侶屬性嘛。

概念

Broadcast an event that propagates downward to all descendants of the current instance. Since the descendants expand into multiple sub-trees, the event propagation will follow many different “paths”. The propagation for each path will stop when a listener callback is fired along that path, unless the callback returns true.

broadcast 是一個事件,它向下傳播到當前實例的所有后代。由于后代擴展為多個子樹,事件傳播將會遵循許多不同的“路徑”。 除非回調返回 true,否則在沿該路徑觸發偵聽器回調時,每個路徑的傳播將會停止。

參數

broadcast 會接收兩中參數:event 是事件名稱,[...args] 是觸發事件時傳遞給回調函數的參數。

例子

// 創建 parent 組件實例var parent = new Vue()// 創建 child1 組件實例,其父組件指向 parentvar child1 = new Vue({ parent: parent })// 創建 child2 組件實例,其父組件指向 parentvar child2 = new Vue({ parent: parent })// 創建 child3 組件實例,其父組件指向 child2var child3 = new Vue({ parent: child2 })// 在 child1 組件監聽名為 test 的事件,并綁定了一個回調函數child1.$on('test', function () { console.log('child1 notified')})// 在 child2 組件監聽名為 test 的事件,并綁定了一個回調函數child2.$on('test', function () { console.log('child2 notified')})// 在 child3 組件監聽名為 test 的事件,并綁定了一個回調函數child3.$on('test', function () { console.log('child3 notified')})

parent、child1、child2 和 child3 四個組件之間的關系可以展示成如下的關系圖:


parent.$broadcast('test')// -> "child1 notified"http:// -> "child2 notified"

當執行 parent.$broadcast('test'); 時,事件流會以 parent 組件為起點向 parent 的子組件進行傳遞,根據事件綁定的順序,雖然 parent 組件有兩個同級的 child1 和 child2 ,但是事件流會先觸發 child1 里面的綁定事件,此時會輸出 "child1 notified",然后事件流到達 child2 組件,會觸發 child2 組件中的綁定事件,輸出 "child2 notified"。到這時,child2 組件中的偵聽器并沒有返回 true,所以事件傳遞到此就結束了,最終的輸出結果就只有 "child1 notified" 和 "child2 notified"。

Vue 1.0 官方實現

在 Vue 1.0 版本中,$broadcast 實現的源碼放在 /src/instance/api/events.js 文件中,代碼很簡單:

/** * Recursively broadcast an event to all children instances. * 遞歸地向所有子實例廣播事件。 * @param {String|Object} event * @param {...*} additional arguments */// $dispatch 方法是定義在 Vue 的 prototype 上的// 接受一個事件Vue.prototype.$broadcast = function (event) { // 獲取傳入事件的類型,判斷是否為字符串 var isSource = typeof event === 'string'  // 校正 event 的值,當接受 event 的類型為字符串時就直接使用,如果不是字符串就使用 event 上的 name 屬性  event = isSource ? event : event.name  // if no child has registered for this event, // then there's no need to broadcast. // 如果當前組件的子組件沒有注冊該事件,就直接返回,并不用 broadcast if (!this._eventsCount[event]) return  // 獲取當前組件的子組件 var children = this.$children  // 將函數接受的參數轉換成數組 var args = toArray(arguments)  // 如果傳入事件為字符串 if (isSource) {  // use object event to indicate non-source emit  // on children  // 根據傳入的事件名稱的參數組裝成 object  args[0] = { name: event, source: this } }  // 循環子組件 for (var i = 0, l = children.length; i < l; i++) {  var child = children[i]    // 在每個子組件中調用 $emit 觸發事件  var shouldPropagate = child.$emit.apply(child, args)    // 判斷調用 $emit 返回的值是否為 true  if (shouldPropagate) {   // 如果調用 $emit 返回的值為 true,就遞歸孫子組件繼續廣播   child.$broadcast.apply(child, args)  } }  // 最后返回當前組件的實例 return this}

element-ui 實現

在 element-ui 中,$broadcast 實現的源碼放在 /src/mixins/emitter.js 文件中,代碼很簡單:

// 定義 broadcast 方法,接受三個參數,分別是:組件名稱、將要觸發的事件名稱、回調函數傳遞的參數function broadcast(componentName, eventName, params) { // 依次循環當前組件的子組件 this.$children.forEach(child => {  // 獲取每個子組件的名字  var name = child.$options.componentName;  // 判斷子組件的名字是否等于傳入的組件名稱  if (name === componentName) {   // 如果子組件的名字等于傳入的組件名稱就調用 $emit 觸發事件   child.$emit.apply(child, [eventName].concat(params));  } else {   // 如果子組件的名字不等于傳入的組件名稱就遞歸遍歷調用 broadcast 孫子組件   broadcast.apply(child, [componentName, eventName].concat([params]));  } });}

差異分析

和之前說到的 $dispatch 一樣,這里的 $broadcast 的兩個實現版本也存在著巨大的差異:

1、接受參數:Vue 實現版本只會接受一個字符串類型的事件名稱為參數,而 element-ui 實現的版本會接受三個參數,分別是:需要觸發事件的組件名稱、將要觸發的事件名稱、回調函數傳遞的參數;

2、實現功能:Vue 實現的 $broadcast 觸發方式是默認只觸發子代組件,不觸發孫子代組件,如果子代創建了監聽且返回了true,才會向孫子代組件傳遞事件。而 element-ui 實現的版本是直接向所有子孫后代組件傳遞,直至獲取到的子組件名稱等于傳入的組件名稱相等,才會觸發當前子組件的監聽事件,期間也沒有返回值的判定。

11 總結

說到這里,$dispatch 和 $broadcast 的講解就結束了??赡艽蠹乙呀浿懒?Vue 2.0 版本為什么會將這兩個屬性移除。首先我們引入官網的說法:

因為基于組件樹結構的事件流方式實在是讓人難以理解,并且在組件結構擴展的過程中會變得越來越脆弱。這種事件方式確實不太好,我們也不希望在以后讓開發者們太痛苦。并且 $dispatch 和 $broadcast 也沒有解決兄弟組件間的通信問題。

這樣來說 $dispatch 和 $broadcast 確實會有這樣的問題。在前面的講解中,大家也不難發現 $dispatch 主要是事件流由當前組件往父組件流動,當滿足一定條件的時候就會觸發當前子組件的監聽事件,$broadcast 的功能是事件流由當前組件向子組件流動,當滿足一定條件的時候就會觸發當前子組件的監聽事件。也就是說 $dispatch 和 $broadcast 主要解決了父子組件、嵌套父子組件的通信,并沒有解決兄弟組件的通信問題,另一個方面這樣的事件流動的方式是基于組件樹結構的,當業務越來越煩雜時,這種方式會顯得極其繁瑣,甚至會混亂到難以維護,所以 Vue 2.0 版本移除這兩個 API 是在意料之中的。

但是為什么三方 UI 庫都會封裝類似的這樣一個組件通信的方式呢?我的猜測可能是為了解決在父子層嵌套組件中,通過 $dispatch 和 $broadcast 定向的向某個父或者子組件遠程調用事件,這樣就避免了通過傳 props 或者使用 refs 調用組件實例方法的操作。這樣說的話,$dispatch 和 $broadcast 也就其存在的價值,而并不是一無是處的,還是那句話:技術沒有好與壞,只有合適不合適!

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
97国产精品视频| 日韩欧美国产一区二区| 青青草99啪国产免费| 精品亚洲一区二区三区| 亚洲精品久久视频| 亚洲人成电影在线播放| 亚洲一区亚洲二区亚洲三区| 精品久久久久国产| 国产精品美女免费| 国产午夜精品视频| 色综合久久久久久中文网| 中文字幕精品—区二区| 欧美怡春院一区二区三区| 最近2019年好看中文字幕视频| 欧美插天视频在线播放| 欧美日韩免费区域视频在线观看| 国产一区香蕉久久| 久久久久久亚洲精品中文字幕| 亚洲第一区中文99精品| 国产日韩中文在线| 亚洲国产精品人人爽夜夜爽| 国产在线拍揄自揄视频不卡99| 91夜夜未满十八勿入爽爽影院| 国产97在线|日韩| 精品国产31久久久久久| 懂色av中文一区二区三区天美| 亚洲老头同性xxxxx| 国产成人小视频在线观看| 久久久精品中文字幕| 日本一区二区不卡| 亚洲天堂2020| 欧美性猛交99久久久久99按摩| 国产亚洲欧洲在线| 欧美成人午夜剧场免费观看| 日本精品久久久久影院| 精品亚洲国产成av人片传媒| 亚洲色图日韩av| 亚洲无亚洲人成网站77777| 91精品一区二区| 麻豆国产va免费精品高清在线| 国外成人性视频| 国产精品99久久久久久白浆小说| 一区二区亚洲精品国产| 欧美极品少妇全裸体| 国产精品扒开腿爽爽爽视频| 日韩视频免费中文字幕| 欧美中文字幕在线视频| 国产精品视频中文字幕91| 伊人久久久久久久久久| 欧美高清在线观看| 日本成熟性欧美| 日韩在线观看网站| 91tv亚洲精品香蕉国产一区7ujn| 欧美精品免费播放| 国产一区二区久久精品| 日韩激情av在线免费观看| 欧美激情在线观看| 国产在线观看精品一区二区三区| 亚洲天堂网在线观看| 色综合天天狠天天透天天伊人| 国产精品69精品一区二区三区| 91国产精品视频在线| 亚洲欧美日韩一区在线| 中文字幕久热精品视频在线| 日韩av大片在线| 日韩免费电影在线观看| 久久精品国产精品| 欧美激情视频播放| 日本电影亚洲天堂| 精品国产成人av| 欧美另类高清videos| 日韩中文字幕久久| 91色琪琪电影亚洲精品久久| 欧美日韩精品在线播放| 国产ts一区二区| 欧美精品久久一区二区| 日韩av在线免费| 美女视频久久黄| 日韩a**站在线观看| 亚洲男女性事视频| 亚洲小视频在线观看| 国产成人精品免高潮在线观看| 欧美日韩在线影院| 欧美国产日韩一区二区三区| 伊人久久久久久久久久久久久| 欧美剧在线观看| 国产成人精品综合| 日韩中文字幕在线免费观看| 久久人人爽人人爽人人片av高请| 欧美极品少妇xxxxⅹ裸体艺术| 亚洲精品国精品久久99热一| 欧美久久精品午夜青青大伊人| 亚洲三级 欧美三级| 日本亚洲欧美成人| 日韩av在线高清| 韩国欧美亚洲国产| 欧美日韩国产一中文字不卡| 青青a在线精品免费观看| 国产精品久久久久久久久久ktv| 亚洲国产女人aaa毛片在线| 国产精品久久久久久久久久ktv| 亚洲free性xxxx护士hd| 最近免费中文字幕视频2019| 精品一区二区三区电影| 日韩av综合中文字幕| 日本一区二三区好的精华液| 欧美大片第1页| 亚洲小视频在线| 成人av色在线观看| 国产精品劲爆视频| 久久青草精品视频免费观看| 欧美成人精品在线播放| 亚洲色图五月天| 欧美亚洲另类在线| 亚洲欧美日韩在线高清直播| 欧美在线视频免费播放| 国内精品久久影院| 国产精品视频久久| 国产精品福利小视频| 欧美激情aaaa| 在线播放日韩欧美| 午夜精品久久久久久久99热| 亚洲成人精品久久久| 亚洲欧美在线磁力| 午夜精品久久久99热福利| 亚洲第一精品夜夜躁人人爽| 国模精品视频一区二区三区| 欧美午夜久久久| 狠狠综合久久av一区二区小说| 精品日本美女福利在线观看| 5566成人精品视频免费| 美女av一区二区| 日韩av免费看| 国产精品激情av电影在线观看| 欧美性xxxx极品hd满灌| 亚洲欧美综合图区| 欧美成人一区二区三区电影| 久久视频这里只有精品| 亚洲高清免费观看高清完整版| 一区二区亚洲精品国产| 亚洲欧美日韩区| 国产视频久久久久久久| 国产精品精品久久久久久| 亚洲91精品在线观看| 欧美另类交人妖| 国产精品96久久久久久又黄又硬| 日韩福利视频在线观看| 国产成人精品亚洲精品| 国产精品手机播放| 久久不射热爱视频精品| 国产美女精彩久久| 成人激情av在线| 欧美夫妻性视频| 日韩久久午夜影院| 宅男66日本亚洲欧美视频| 久久久天堂国产精品女人| 日韩精品在线视频美女| 亚洲欧美国产日韩天堂区| 亚洲欧美日韩第一区| 宅男66日本亚洲欧美视频| 欧美亚洲国产成人精品| 久久久av一区| 欧美又大粗又爽又黄大片视频| 国产精品成人aaaaa网站|