Vue.js 3 Автобус событий

Как создать шину событий в Vue 3?


В Vue 2 это было:

export const bus = new Vue();
bus.$on(...)
bus.$emit(...)

В Vue 3 Vue больше не является конструктором, а Vue.createApp({}); возвращает объект, у которого нет методов $on и $emit.


person KeyKi    schedule 18.08.2020    source источник
comment
RFC предлагает вместо этого использовать стороннюю библиотеку: github.com/vuejs/rfcs/blob/master/active-rfcs/   -  person skirtle    schedule 18.08.2020
comment
Можете ли вы уточнить, для каких функций вам это нужно? В некоторых случаях подойдет даже ref.   -  person Daniel    schedule 18.08.2020
comment
Иметь один общий для всех компонентов объект, чтобы генерировать и прослушивать события на нем. (Для этого используется шина событий.)   -  person KeyKi    schedule 19.08.2020


Ответы (4)


Как предлагается в официальных документах, вы можете использовать mitt библиотека для отправки событий между компонентами, предположим, что у нас есть боковая панель и header, которая содержит кнопку, закрывающую/открывающую боковая панель, и нам нужна эта кнопка для переключения некоторого свойства внутри компонента боковой панели:

в main.js импортируйте эту библиотеку и создайте экземпляр этого эмиттера и определите его как глобальное свойство:

Установка:

npm install --save mitt

Использование:

import { createApp } from 'vue'
import App from './App.vue'
import mitt from 'mitt';
const emitter = mitt();
const app = createApp(App);
app.config.globalProperties.emitter = emitter;
app.mount('#app');

в заголовке генерируйте событие toggle-sidebar с некоторой полезной нагрузкой:

<template>
  <header>
    <button @click="toggleSidebar"/>toggle</button>
  </header>
</template>
<script >
export default { 
  data() {
    return {
      sidebarOpen: true
    };
  },
  methods: {
    toggleSidebar() {
      this.sidebarOpen = !this.sidebarOpen;
      this.emitter.emit("toggle-sidebar", this.sidebarOpen);
    }
  }
};
</script>

На боковой панели получите событие с полезной нагрузкой:

<template>
  <aside class="sidebar" :class="{'sidebar--toggled': !isOpen}">
  ....
  </aside>
</template>
<script>
export default {
  name: "sidebar",
  data() {
    return {
      isOpen: true
    };
  },
  mounted() { 
    this.emitter.on("toggle-sidebar", isOpen => {
      this.isOpen = isOpen;
    });
  }
};
</script>

Для тех, кто использует составной API, они могут использовать emitter следующим образом:

Создайте файл src/composables/useEmitter.js

import { getCurrentInstance } from 'vue'

export default function useEmitter() {
    const internalInstance = getCurrentInstance(); 
    const emitter = internalInstance.appContext.config.globalProperties.emitter;

    return emitter;
}

И с этого момента вы можете использовать useEmitter так же, как и с useRouter:

import useEmitter from '@/composables/useEmitter'

export default {
  setup() {
    const emitter = useEmitter()
    ...
  }
  ...
}
person Boussadjra Brahim    schedule 22.09.2020
comment
Отличная работа ! У меня работает сейчас 21.03.2021. - person felipe muner; 21.03.2021
comment
Работал для меня с Composition API - person Anirudh; 29.06.2021
comment
Идеальное простое легкое объяснение! Благодарю вас. - person LUISAO; 03.07.2021

В версии 3 Vue.js вы можете использовать либо стороннюю библиотеку, либо использовать функциональность, написанную в шаблоне программирования издатель-подписчик (концепция PubSub).

событие.js

//events - a super-basic Javascript (publish subscribe) pattern

class Event{
    constructor(){
        this.events = {};
    }

    on(eventName, fn) {
        this.events[eventName] = this.events[eventName] || [];
        this.events[eventName].push(fn);
    }

    off(eventName, fn) {
        if (this.events[eventName]) {
            for (var i = 0; i < this.events[eventName].length; i++) {
                if (this.events[eventName][i] === fn) {
                    this.events[eventName].splice(i, 1);
                    break;
                }
            };
        }
    }

    trigger(eventName, data) {
        if (this.events[eventName]) {
            this.events[eventName].forEach(function(fn) {
                fn(data);
            });
        }
    }
}

export default new Event();

index.js

import Vue from 'vue';
import $bus from '.../event.js';

const app = Vue.createApp({})
app.config.globalProperties.$bus = $bus;
person magistr4815    schedule 24.09.2020
comment
Я изменил on на $on, off на $off и trigger на $emit, чтобы лучше соответствовать методам моих шин Vue 2. - person kevnk; 23.03.2021

С композицией Vue и defineEmit вы даже можете упростить задачу:

<!-- Parent -->
<script setup>
  import { defineEmit } from 'vue'
  const emit = defineEmit(['selected'])
  const onEmit = (data) => console.log(data)
</script>

<template>
    <btnList
        v-for="x in y"
        :key="x"
        :emit="emit"
        @selected="onEmit"
    />
</template>
<!-- Children (BtnList.vue) -->
<script setup>
  import { defineProps } from 'vue'
  const props = defineProps({
      emit: Function
  })
</script>

<template>
    <button v-for="x in 10" :key="x" @click="props.emit('selected', x)">Click {{ x }}</button>
</template>

Я только что показал это с одним дочерним элементом, но вы можете передать функцию emit другим дочерним элементам.

person Benjamin Fourgeaud    schedule 11.05.2021

В проекте Vuejs вы можете создать новый файл в том же корне, что и App.vue, и создать экземпляр конструктора Vue.

файл eventBus.js

import Vue from 'vue';
export default new Vue();

Затем вам нужно только импортировать событие в каждый файл vue, который вы хотите отправить или получить, например:

import EventBus from "../eventBus.js";

Чтобы просто отправить и получить событие, используйте отметку $emit/$on

EventBus.$emit("evMsg", someData);
EventBus.$on("evMsg", (someData) => {});
person Frederico Gomes    schedule 21.08.2020
comment
Эта тема посвящена Vue 3. Кроме того, в сообщении темы я написал небольшой пример, как создать шину событий в Vue 2. - person KeyKi; 22.08.2020
comment
Я использую Vue 3 с этим подходом. - person Frederico Gomes; 24.08.2020
comment
Я не думаю, что вы - $on был удален: v3.vuejs .org/guide/migration/events-api.html#overview - person David K. Hess; 29.08.2020
comment
Неверный ответ, потому что .$on и .$emit были удалены в VUE3. - person mikep; 10.11.2020
comment
Но у EventBus могут быть свои $on и $emit? Я прав? - person Čamo; 28.05.2021