使用Vuex的全局事件总线-始终通知订户

时间:2018-12-03 16:32:14

标签: javascript vue.js vuex

我在Vue中使用全球事件总线已经有一段时间了-类似于const bus = new Vue()。效果很好,但是订阅的处理可能会变得很冗长。

假设我订阅了一个组件中的事件:

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

我将不得不跟踪回调并将其正确放置在beforeDestroy中。使用全局混合可以稍微简化此操作,但是由于我使用的是<keep-alive>,因此我必须区分在mountedactivated回调中进行的订阅。

所以我认为我会给Vuex一个管理这个问题的机会,因为观察者已被框架处理了。我提出了以下建议。

只要发布对象或数组,似乎就可以正常工作。尽管原始数据被包装在外部对象(即{ data: 123 }

中),但似乎并未触发反应性

我正在寻找有关通知订户的替代解决方案。到目前为止,我所看到的只是内部notify方法,使用起来感觉不太安全。

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}`)
  }
}

4 个答案:

答案 0 :(得分:2)

您可以使用deepCopy(例如JSON.parse(JSON.stringify()))来确保数据是反应性的

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))
  }
}

在上面的组件中,您正在监听器中监听foo_barVue watcher仅适用于组件数据(来自datacomputedvuex的组件数据。

您可以如下将数据重新定义为componentData。您可以使用mapGetters来缩短语法:

<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>

答案 1 :(得分:0)

此API将中断Vuex的数据流,这是Vuex的核心概念。客户将能够在Vuex的任何地方更改/读取存储状态。

老实说,由于Vuex只是一个事件发射器,因此不需要在Vuex中实现。我建议您在操作中使用一些事件发射器(可能是空的Vue实例)。

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)
    }
  }
}

当我在github中搜索时,它解决了我的问题。可以帮助您。谢谢!

答案 2 :(得分:0)

在对象上调用Vue.set不会为对象内部的数据增加观察者/反应性。这需要一个额外的Vue.set

Vue.set(state.events, payload.key, {})
Vue.set(state.events[payload.key], 'data', payload.data)

您也可以将其包装到实用程序函数中,该函数使用Vue.set

来递归设置数据

答案 3 :(得分:-1)

您能尝试一下,让我知道在这两种情况下是否都触发了反应性吗?

首先,删除外部对象的这种不必要的包装,然后将有效负载作为具有所需事件键和该键数据的简单键/值对象发送:

{
  someKey: 123
}

第二步,发送一些嵌套数据:

{
  someKey: {
    nested: 'Value'
  }
}

但是在此之前,请按如下所示更改突变代码:

const mutations = {
  publish_event(state, payload) {
    // Instead of the previous code, just "patch"
    // the state.events with the payload content.
    state.events = { ...state.events, ...payload }
  }
}

不要忘了改进mapEventGetters函数,因为数据不再嵌套在“ data”属性中。

PS:但是我个人不明白为什么不对特定的吸气剂使用Vuex,因为它可以工作,并且会触发与原始类型的反应:

store / index.js

import Vue from 'vue'
import Vuex from 'vuex'

const state = {
  events: {}
}

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

const mutations = {
  publish_event(state, payload) {
    state.events = { ...state.events, ...payload }
  }
}

const getters = {
  fooBar: state => state.events.fooBar || ''
}

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  actions,
  mutations,
  getters
})

main.js

import Vue from 'vue'
import App from '@/App'
import store from '@/store'

new Vue({
  store,
  render: h => h(App)
}).$mount('main')

某些组件

<template>
  <span>{{ fooBar }}</span>
</template>

import { mapGetters, mapActions } from 'vuex'

export default {
  name: 'SomeComponent',

  computed: {
    ...mapGetters(['fooBar'])
  },

  methods: {
    ...mapActions(['publish'])
  },

  created () {
    setTimeout(() => {
      publish({
        fooBar: 123
      })
    }, 3000)
  }
}