[Vue深入組件-邊界情況處理] 程序化的事件監聽器

1. 快速認識

一句話,來說,在同一個Vue實例中$on$once 用於設定監聽器, 不同的是,$once 僅會被觸發一次後立即失效。 而$off 則是一個主動的監聽器銷燬器。 $emit 則用於觸發通過$on,$once 設定的監聽器,即事件觸發器/發射器。

以下是一個簡單的例子:

<template>
  <div>
    <button @click="$emit('cusEvent', 'cusEvent trigger~')">Emit cusEvent</button>
    <button @click="$off('cusEvent')">destory cusEvent</button>
    <br>
    <button @click="$emit('cusOnceEvent','only once chance to trigger~~~')">Emit OnceEvent</button>
  </div>
</template>
<script>
export default {
  created() {
    this.$on("cusEvent", (msg) => {
      console.log(msg,'--line13');
    });
    this.$once("cusOnceEvent",(msg)=>{
      console.log(msg,'--line20');
    })
  },
};
</script>

i

在生命週期created 鉤子中,定義了兩個事件監聽器,分別是$on 監聽的cusEvent$once 監聽的cusOnceEvent.從演示中看到,執行操作的結果符合我們的預期,$once$on 都可以通過$emit 觸發,此外,$once 僅觸發一次,且$on在被$off 銷燬後,不再觸發。

2. 進一步認識, 使用方法

2.1 vm.$on( event, callback )

  • 參數

    • {string | Array<string>} event (數組只在 2.2.0+ 中支持)
    • {Function} callback
  • 用法

    監聽當前實例上的自定義事件。事件可以由 vm.$emit 觸發。回調函數會接收所有傳入事件觸發函數的額外參數。

傳入的監聽函數,並不是只能是單個單個註冊,可以是一個數組,以$on 爲例:

<template>
  <div>
    <button @click="$emit('cusE01','E01')">cusE01</button>
    <button @click="$emit('cusE02','E02','hello')">cusE02</button>
    <button @click="$emit('cusE03','E03','hello','world')">cusE03</button>
  </div>
</template>
<script>
export default {
  created(){
    this.$on(['cusE01','cusE02','cusE03'],(...params)=>{
      console.log(params,'--line12');
      // ['E01'] '--line12'
      // ['E02', 'hello'] '--line12'
      // ['E03', 'hello', 'world'] '--line12'
    })
  }
};
</script>
<style></style>

2.2 vm.$once( event, callback )

  • 參數

    • {string} event
    • {Function} callback
  • 用法

    監聽一個自定義事件,但是隻觸發一次。一旦觸發之後,監聽器就會被移除。

注意,$once 接收參數僅可爲一個,不可爲數組,若爲數組,在觸發任意一個監聽器後,全部即刻失效。

如下示例:

<template>
  <div>
    <button @click="$emit('cusOnceE01','OnceE01')">cusOnceE01</button>
    <button @click="$emit('cusOnceE02','OnceE02','hello')">cusOnceE02</button>
    <button @click="$emit('cusOnceE03','OnceE03','hello','world')">cusOnceE03</button>
  </div>
</template>
<script>
export default {
  created(){
    this.$once(['cusOnceE01','cusOnceE02','cusOnceE03'],(...params)=>{
      console.log(params,'--line12');
      //  以下輸出僅在相應按鈕點擊時觸發一次,立刻失效,即以下輸出只能單次輸出其中一個
      // ['OnceE01'] '--line12'
      // 或
      // ['OnceE02', 'hello'] '--line12'
      // 或
      // ['OnceE03', 'hello', 'world'] '--line12'
    })
  }
};
</script>
<style></style>

2.3 vm.$off( [event, callback] )

  • 參數

    • {string | Array<string>} event (只在 2.2.2+ 支持數組)
    • {Function} [callback]
  • 用法

    移除自定義事件監聽器。

    • 如果沒有提供參數,則移除所有的事件監聽器;
    • 如果只提供了事件,則移除該事件所有的監聽器;
    • 如果同時提供了事件與回調,則只移除這個回調的監聽器。

2.3.1 同時銷燬多個監聽器

指定銷燬:$off 傳入一個包含監聽器事件名的數組作爲第一個參數時,將可以指定的銷燬當前實例多個定時器。

全部銷燬:$off 不傳入任何參數時,將移除當前實例的所有監聽器。

<template>
  <div>
    <button @click="$emit('cusE01','E01')">cusE01</button>
    <button @click="$emit('cusE02','E02','hello')">cusE02</button>
    <button @click="$emit('cusE03','E03','hello','world')">cusE03</button>
    <button @click="$off(['cusE01','cusE02'])">Off Multi</button><!--指定銷燬/移除多個-->
    <button @click="$off()">Off All</button><!--全部銷燬/移除-->
  </div>
</template>
<script>
export default {
  created(){
    this.$on(['cusE01','cusE02','cusE03'],(...params)=>{
      console.log(params,'--line12');
      // ['E01'] '--line12'
      // ['E02', 'hello'] '--line12'
      // ['E03', 'hello', 'world'] '--line12'
    })
  }
};
</script>

2.4 vm.$emit( eventName, […args] )

參數

  • {string} eventName
  • [...args]

觸發當前實例上的事件。附加參數都會傳給監聽器回調。

注意,同時只能觸發一個監聽器事件。

3. 兩個組件傳值可通過事件總線EventBus實現究竟是怎麼回事?

分析:

首先,我們知道,$on$once$emit 等等這些是"實例事件/方法",也就是說,他們是Vue實例上的自帶方法。 我們在<script></script>標籤包裹的部分去使用這些方法時,前面需要帶上this 才能夠正常訪問到。 就像這樣this.$on, this.$emit ...., 我們也知道,this 通常,指向的就是Vue 實例對象。

有了這些認識,我們能不能這樣做,我們創建一個Vue實例,然後在A組件中去動態的給這個實例"埋設"一個監聽器, 然後再在B組件中去動態觸發這些監聽器呢?

實際,這就是Vue EventBus 事件總線的核心思路。 它實際上很簡潔。下面是一個實現的示例:

// bus.js 實例化一個vue對象,並導出:
import Vue from 'vue'
export default new Vue();

再然後再在 A,B 組件中分別去導入:

<script>
import Bus from './bus'
....

A, B 組件分別時這樣去定義的:

<!-- A -->
<template>
  <div>
    <p>Child A</p>
    <button @click="trigger">emit cusEvent</button>
  </div>
</template>

<script>
import Bus from './bus'
export default {
  data() {
    return {
      name: "jayce",
    };
  },
  methods:{
    trigger(){
      Bus.$emit('cusEvent',this.name)
    }
  }
};
</script>
<!-- B -->
<template>
<p>Child B</p>
</template>
<script>
import Bus from './bus'
export default {
  created(){
    Bus.$on('cusEvent',(par)=>{
      console.log(par,'--line7');
    })
  }
}
</script>    

現在你就能在A組件中通過一個點擊事件,將name 這個變量通過自定義事件傳參的方式從A傳遞到B。
簡單的描述一下:

我們在B組件中,週期函數created中,通過Bus.$on 給bus.js "埋設"了一個自定義事件,名爲"cusEvent", 且定義了一個形參par 在其回調函數中。

緊接着,我們在A組件中,通過點擊事件觸發了bus.js 中埋設的事件"cusEvent", 並傳入了一個實參name

這樣,知道點擊了A組件中的該按鈕,就會觸發我們的自定義事件,然後通過回調函數的參數接收到“傳參”。

可以簡單總結下這個示例:

  1. "發送參數" 通過$emit 觸發監聽器實現,"接受參數"通過$on 監聽事件被觸發後回調所傳入的值得到。
  2. 你會發現,實際上事件總線,只和兩個單獨的組件有關,所以理論上,你可以實現任意兩個組件之間的傳值。而不僅僅是兄弟傳值。

【注意事項】:

在該示例中,A組件中,去觸發的時候,不能這樣去寫:

<template><div><p>Child A</p><button @click="Bus.$emit('cusEvent',name)">emit cusEvent</button></div></template><script>import Bus from './bus'export default {data() {return { name: "jayce",};},};</script>

直接把點擊事件觸發內容定義在Dom上是不可以的,會報錯誤找不到Bus, 而出現這個問題的原因是因爲Vue組件的渲染週期導致的。 Vue 組件的dom渲染是異步執行的。 直到mounted才完成渲染,有可能Bus還沒有被import 進來,就渲染解析到了dom上的Bus.$emit.... 所以,就找不到,會報錯。

我們將點擊事件觸發內容定義在methods中,dom在渲染時,就只會找methods中存不存在對應的方法,在上例中,就是trigger這個方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章