Vue emit & on 探討

一、背景

基于Vue父子組件的通信規(guī)則,我發(fā)現(xiàn)我對父子組件通信出現(xiàn)了誤解棍辕。首先父組件通過v-bind綁定參數(shù)到子組件身上,然后在子組件內(nèi)通過“emit”还绘,將相應(yīng)的事件“拋出去”楚昭,然后在父組件內(nèi)通過相應(yīng)的事件來接收處理。
這里的問題在于我把這個事情弄錯了拍顷,我一直以為是在子組件內(nèi)注冊相應(yīng)的方法抚太,然后在父組件內(nèi)調(diào)用,其實不然昔案,其實這是一個發(fā)布訂閱模式尿贫。發(fā)布與訂閱都是基于組件本身的,并不能跨組件調(diào)用踏揣,而是由于我們父子組件通信的時候庆亡,發(fā)布的位置與訂閱的位置剛好在邏輯上隔了一層,導致的理解偏差捞稿。我們在子組件本身上注冊了一個事件又谋,形如@eventName=“handler”的形式,這個相當于是在向子組件身上添加訂閱者娱局,然后我們又在子組件內(nèi)部通過this.$emit(‘eventName’, params)彰亥,“發(fā)射”觸發(fā)這個事件,讓父組件內(nèi)部調(diào)用相應(yīng)的方法來處理铃辖。

舉個例子:

mounted() {
    this.$on('eventName', (val) => {
      console.log(val)
    })

    this.$emit('eventName', 'hello world')
}

這串代碼先是在mounted生命周期里注冊了一個事件剩愧,然后通過emit觸發(fā)了這個事件,于是我們運行之后將會在瀏覽器控制臺打印以下結(jié)果:


hello world.png

二娇斩、深入思考??

以下是Vue的原型上綁定的on和emit兩個方法的源碼仁卷,可以看出大佬們寫代碼就是飄逸,考慮周全犬第。這兩個函數(shù)其實就實現(xiàn)了發(fā)布訂閱锦积,通過this的隱式綁定,把事件中心綁定到相應(yīng)的組件上歉嗓,由他來統(tǒng)一處理相應(yīng)的事件的發(fā)布與訂閱丰介。

Vue.prototype.$on = function (event, fn) {
    var vm = this;
    if (Array.isArray(event)) {
      for (var i = 0, l = event.length; i < l; i++) {
        vm.$on(event[i], fn);
      }
    } else {
      (vm._events[event] || (vm._events[event] = [])).push(fn);
      // optimize hook:event cost by using a boolean flag marked at registration
      // instead of a hash lookup
      if (hookRE.test(event)) {
        vm._hasHookEvent = true;
      }
    }
    return vm
  };
Vue.prototype.$emit = function (event) {
    var vm = this;
    if (process.env.NODE_ENV !== 'production') {
      var lowerCaseEvent = event.toLowerCase();
      if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
        tip(
          "Event \"" + lowerCaseEvent + "\" is emitted in component " +
          (formatComponentName(vm)) + " but the handler is registered for \"" + event + "\". " +
          "Note that HTML attributes are case-insensitive and you cannot use " +
          "v-on to listen to camelCase events when using in-DOM templates. " +
          "You should probably use \"" + (hyphenate(event)) + "\" instead of \"" + event + "\"."
        );
      }
    }
    var cbs = vm._events[event];
    if (cbs) {
      cbs = cbs.length > 1 ? toArray(cbs) : cbs;
      var args = toArray(arguments, 1);
      var info = "event handler for \"" + event + "\"";
      for (var i = 0, l = cbs.length; i < l; i++) {
        invokeWithErrorHandling(cbs[i], vm, args, vm, info);
      }
    }
    return vm
  };

基于這個原理,我們可以實現(xiàn)兩個工具函數(shù),這也是iview里面實現(xiàn)父子組件跨級通信的一種方式哮幢,一個叫broadcast带膀,用于向子組件廣播事件,另一個叫dispatch橙垢,相反的是向父組件傳遞事件垛叨。

function broadcast(componentName, eventName, params) {
  this.$children.forEach(child => {
    const name = child.$options.name

    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params))
    } else {
      // todo 如果 params 是空數(shù)組,接收到的會是 undefined
      broadcast.apply(child, [componentName, eventName].concat([params]))
    }
  })
}
function dispatch(componentName, eventName, params) {
      let parent = this.$parent || this.$root
      let name = parent.$options.name

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent

        if (parent) {
          name = parent.$options.name
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params))
      }
}

這樣我們就可以在子組件里通過改變了相應(yīng)的事件而向其特定的父組件發(fā)射觸發(fā)事件柜某,但是必須先在父組件里通過$on注冊相應(yīng)的事件嗽元,以及回調(diào)函數(shù)。
這種方式是特別有效的喂击,可以封裝為一個工具函數(shù)剂癌。vue1.0中也實現(xiàn)了這兩個方法,但有點不一樣翰绊,它是通過是否返回true來判斷是否繼續(xù)“冒泡”佩谷,向上或者向下傳遞事件流。但是這些方法在vue2.0中已經(jīng)廢除了辞做。

除此之外琳要,我們還可以通過事件總線(eventBus)實現(xiàn)兄弟組件通信或其他跨級通信,這個原理就是通過在vue上掛載一個新的vue實例秤茅,然后在各個組件中引用它來發(fā)布和訂閱相應(yīng)的事件來通信稚补。

三、總結(jié)

對于框架的學習不要停留在表面框喳,一個工具怎么使用永遠是最基本的要求课幕,但是明白了工具內(nèi)部的具體邏輯,就能更加靈活的使用它五垮。遇到相應(yīng)的問題最好先自己思索乍惊,多看源碼,Vue內(nèi)部有很多好的代碼放仗,好的機制润绎,像我們使用的組件庫的源碼也可以看看,別人的CSS寫法诞挨,函數(shù)寫法莉撇,為什么要這么寫,都是值得我們思考學習的惶傻。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末棍郎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子银室,更是在濱河造成了極大的恐慌涂佃,老刑警劉巖励翼,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辜荠,居然都是意外死亡汽抚,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門侨拦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來殊橙,“玉大人辐宾,你說我怎么就攤上這事狱从。” “怎么了叠纹?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵季研,是天一觀的道長。 經(jīng)常有香客問我誉察,道長与涡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任持偏,我火速辦了婚禮驼卖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鸿秆。我一直安慰自己酌畜,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布卿叽。 她就那樣靜靜地躺著桥胞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪考婴。 梳的紋絲不亂的頭發(fā)上贩虾,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音沥阱,去河邊找鬼缎罢。 笑死,一個胖子當著我的面吹牛考杉,可吹牛的內(nèi)容都是我干的策精。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奔则,長吁一口氣:“原來是場噩夢啊……” “哼蛮寂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起易茬,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤酬蹋,失蹤者是張志新(化名)和其女友劉穎及老,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體范抓,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡骄恶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了匕垫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片僧鲁。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖象泵,靈堂內(nèi)的尸體忽然破棺而出寞秃,到底是詐尸還是另有隱情,我是刑警寧澤偶惠,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布春寿,位于F島的核電站,受9級特大地震影響忽孽,放射性物質(zhì)發(fā)生泄漏绑改。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一兄一、第九天 我趴在偏房一處隱蔽的房頂上張望厘线。 院中可真熱鬧,春花似錦出革、人聲如沸造壮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽费薄。三九已至,卻和暖如春栖雾,著一層夾襖步出監(jiān)牢的瞬間楞抡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工析藕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留召廷,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓账胧,卻偏偏與公主長得像竞慢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子治泥,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355