Глобальная шина событий Vue.js

Я пытаюсь создать глобальную шину событий так, чтобы два компонента sibling могли взаимодействовать друг с другом. Я искал вокруг; однако я не могу найти примеров того, как его реализовать. Это то, что у меня есть до сих пор:

var bus = new Vue();

Vue.component('Increment', {
  template: "#inc",
  data: function() {
   return ({count: 0})
  },
  methods: {
    increment: function(){
      var increment = this.count++
      bus.$emit('inc', increment)
  }
 }
})

Vue.component('Display', {
  template: "#display",
  data: function(){
  return({count: 0})
  },
 created: function(){
   bus.$on('inc', function(num){
   alert(num)
   this.count = num;
  });
 }
})


vm = new Vue({
 el: "#example",
})

Я создал свои шаблоны так: http://codepen.io/p-adams/pen/PzpZBg

Я хотел бы, чтобы компонент Increment передавал счету компоненту Display. Я не уверен, что я делаю неправильно в bus.$on().

Ответы

Ответ 1

Проблема в том, что в вашей функции bus.$on On this относится к шине. Вам просто нужно привязать текущий экземпляр Vue к этой функции, используя .bind():

bus.$on('inc', function(num){
 alert(num)
 this.count = num;
}.bind(this));

Вам также следует проверить https://github.com/vuejs/vuex, если вы хотите управлять глобальными состояниями приложений.

РЕДАКТИРОВАТЬ: Поскольку эта страница, кажется, получает много кликов, я хочу отредактировать и добавить другой метод, согласно ChristopheMarois в комментариях:

РЕДАКТИРОВАТЬ: В попытке сделать этот ответ немного яснее, и поэтому будущие читатели не должны читать комментарии здесь, что происходит:

Использование жирной стрелки, как показано ниже, связывает лексическую область "this" с компонентом, а не с шиной событий.

bus.$on('inc', (num) => {
 alert(num);
 this.count = num;
});

Или удаление оповещения:

bus.$on('inc', (num) => this.count = num);

Ответ 2

Когда вы пишете JavaScript ES5, вы должны знать о том, что то, что вы называете с помощью ключевого слова this, может измениться, в соответствии с областью действия оно вызывается из.

Полезной метафорой, чтобы окунуться в концепцию this, является мысль о фигурных скобках в ES5 как о заборе, которые содержат/связывают ее собственный this.

Когда вы используете this в функции обратного вызова вашей шины событий, this не ссылается на ваш компонент Vue, а на объект шины, который не имеет данных счета, поэтому данные, которые вы ожидаете обновить, не.

Если у вас есть/хотите написать синтаксис ES5, общее обходное решение (помимо привязки this, как предложено принятым ответом), нужно назначить ключевое слово this такой переменной:

created: function(){
  var self = this;
  bus.$on('inc', function(num){
    alert(num)
    self.count = num;
  });
}

Если вы можете написать ES6, сделайте это, когда это возможно. Вы всегда можете скомпилировать/перевести на ES5 с помощью Babel. Принятый ответ показывает, как с помощью функций стрелок.

Функции стрелок работают в этом случае, потому что они не связывают свои собственные this.

Чтобы придерживаться метафоры ограждения: представьте себе стрелку ES6, выталкивающую отверстие в вашей функции, поэтому внешний this может пройти, и вы можете вызвать this по назначению.

Чтобы узнать больше о функциях стрелок ES6, посетите: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Ответ 3

Это ответ длинным, вот мое решение, использующее vue.js-2

main.js

import Vue from 'vue'
import App from './App'

export const eventBus = new Vue({
  methods:{
    counter(num) {
      this.$emit('addNum', num);
    }
  }
});

new Vue({
  el: '#app',
  template: '<App/>',
  components: { App }
});

comp1.vue

//Calling my named export
import { eventBus } from '../../main'
<template>
  <div>
    <h1>{{ count }}</h1>
    <button @click="counterFn">Counter</button>
  </div>
</template>

<script>
  import { eventBus } from '../../main'

  export default {
    name: 'comp-one',
    data() {
      return {
        count: 0
      }
    },
    methods: {
      counterFn() {
        eventBus.counter(this.count);
      }
    },
    created() {
      eventBus.$on('addNum', () => {
        this.count++;
      })
    }
  }
</script>

Ответ 4

Как насчет этого? Предположим, что Vue.js 2.

Создайте повторно используемый компонент Event-Bus и привяжите его к Vue с помощью шаблона плагина:

// ./components/EventBus.vue
import Vue from 'vue'
export const EventBus = new Vue()

// ./plugins/EventBus.js
export default {
  install(Vue) {
    const { EventBus } = require('../components/EventBus')
    Vue.prototype.$bus = EventBus
  }
}

// ./main.js
import EventBus from './plugins/EventBus'
Vue.use(EventBus)

Затем вы можете делать в любом месте своего кода: this.$bus.$emit('some-event', payload)

В качестве побочного примечания попробуйте использовать шаблон Event-Bus в качестве последнего средства.