[Vuejs]-Audio DOM created inside vuex is not reactive

4👍

Vue only officially supports plain JavaScript objects as reactive data; things like DOM elements and other such objects won’t be and cannot be made reactive.

One way around this is to store just the audioSrc string and isAudioPlaying boolean values in the Vuex store and then manage the Audio object in the root Vue component. You can watch audioSrc and isAudioPlaying for changes and then call methods on the Audio object in response to those changes.

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    audioUrl: null,
    isAudioPlaying: false,
  },
  
  mutations: {
    play(state, url) {
      state.audioUrl = url
      state.isAudioPlaying = true
    },
    
    pause(state) {
      state.isAudioPlaying = false
    }
  }
})

new Vue({
  el: '#app',
  store,
  
  created() {
    this.audio = new Audio()
  },
  
  watch: {
    '$store.state.audioUrl'(url) {
      this.audio.pause()
      this.audio.src = url
      this.audio.currentTime = 0
      this.audio.play()
    },
    
    '$store.state.isAudioPlaying'(playing) {
      if (playing) {
        this.audio.play()
      } else {
        this.audio.pause()
      }
    }
  },
  
  methods: {
    play(url) {
      this.$store.commit('play', url)
    },
    pause() {
      this.$store.commit('pause')
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.3/vuex.js"></script>

<div id="app">
  <button @click="play('https://upload.wikimedia.org/wikipedia/commons/5/5b/Ludwig_van_Beethoven_-_Symphonie_5_c-moll_-_1._Allegro_con_brio.ogg')">Play Beethoven</button>
  <button @click="pause">Pause</button>
  
  <div>audioUrl: {{ $store.state.audioUrl }}</div>
  <div>isAudioPlaying: {{ $store.state.isAudioPlaying }}</div>
</div>

Leave a comment