web-dev-qa-db-fra.com

Bus d'événements global utilisant Vuex - informez toujours les abonnés

J'utilise un bus d'événements mondial depuis un certain temps dans Vue - quelque chose comme const bus = new Vue(). Fonctionne bien, cependant, la disposition des abonnements peut devenir assez prolixe.

Supposons que je m'abonne à un événement dans un composant:

mounted() {
  bus.$on('some.event', callback)
}

Je devrais garder une trace du rappel et le jeter correctement dans beforeDestroy. Cela peut être quelque peu simplifié en utilisant un mixin global, mais puisque j'utilise <keep-alive>, je dois faire la différence entre les abonnements effectués dans les rappels mounted et activated.

J'ai donc pensé que je donnerais à Vuex une chance de gérer cela, car les observateurs sont disposés par le cadre. Je suis venu avec la proposition ci-dessous. 

Semble fonctionner correctement tant que les objets ou les tableaux sont publiés. Les données primitives ne semblent pas déclencher la réactivité, bien qu’elles soient encapsulées dans l’objet externe, c.-à-d. { data: 123 }

Je cherche des solutions alternatives en ce qui concerne la notification aux abonnés. Tout ce que j’ai vu jusqu’à présent, c’est la méthode interne notify, qui n’est pas très sûre d’être utilisée.

eventstore.js

import Vue from 'vue'

const state = {
  events: {}
}

const actions = {
  publish({commit}, payload) {
    commit('publish_event', payload)
  }
}

const mutations = {
  publish_event(state, payload) {
    if(!state.events[payload.key]) {
      Vue.set(state.events, payload.key, { data: payload.data })
    } else {
      state.events[payload.key] = { data: payload.data }
    }
  }
}

const getters = {
  events: state => state.events
}

export default {
  state,
  actions,
  mutations,
  getters
}

globalmixin.js

methods: {
  publish(key, data) {
    this.$store.dispatch('publish', { key, data })
  }
}

somecomponent.vue

function mapEventGetters(eventKeys) {
  return _.reduce(eventKeys, (result, current) => {
    result[current] = function() {
      return  _.get(this, `$store.getters.events.${current}.data`)
    }
    return result
  }, {})
}
computed: {
  ...mapEventGetters(['foo_bar'])
},
watch: {
  'foo_bar'(value) {
    console.log(`foo_bar changed to ${value}`)
  }
}
10
Johan

Vous pouvez utiliser deepCopy (par exemple JSON.parse(JSON.stringify())) pour vous assurer que les données sont réactives.

const mutations = {
  publish_event(state, payload) {
    if(!state.events[payload.key]) {
      state.events[payload.key] = { data: payload.data }
    } else {
      state.events[payload.key] = Object.assign({}, state.events[payload.key], { data: payload.data })
    }
    state.events = JSON.parse(JSON.stringify(state.events))
  }
}

Dans votre composant ci-dessus, vous écoutez foo_bar dans Watcher. Vue watcher ne fonctionne qu'avec les données de composant (de data, computed ou vuex).

Vous pouvez redéfinir vos données en tant que componentData comme ci-dessous. Vous pouvez utiliser mapGetters pour une syntaxe plus courte:

<script>
  import { mapGetters } from 'vuex'
  export default {
    ...mapGetters(['events']),
    computed: {
      componentData () {
        const eventKeys = ['foo_bar']
        return _.reduce(eventKeys, (result, current) => {
          result[current] = function() {
            return  _.get(this, `events.${current}.data`)
          }
          return result
        }, {})
      }
    },
    watch: {
      componentData: function (newVal, oldVal) {
        ...
      }
    }
  }
</script>
2
ittus

Cette API interrompra le flux de données de Vuex, qui est le concept de base de Vuex. Les clients seraient en mesure de muter/de lire l’état du magasin partout dans Vuex.

Honnêtement, cette façon de faire n'est pas nécessaire pour être implémentée dans le Vuex puisqu'il s'agit simplement d'un émetteur d'événement. Je vous suggère d'utiliser un émetteur d'événement (probablement une instance Vue vide) dans les actions.

export const emitter = new Vue()

export default {
  // ...

  actions: {
    // should be called when the store is initialized
    // to observe events
    observe({ dispatch, commit }) {
      emitter.$on('some-event', () => {
        commit('someEvent')
      })

      emitter.$on('other-event', () => {
        dispatch('otherEvent')
      })
    },

    // notify some event in action
    notify({ state }) {
      emitter.$emit('notify', state.someValue)
    }
  }
}

Cela résout mon problème une fois quand je cherche dans github. Mat soit vous aider. Merci!!

0
Ismoil Shifoev