1

In a Vue.js app, I have two sibling components: a map view and a status bar. The status bar shows, amongst other things, the current geographical coordinates of the mouse pointer.

Currently I have a mousemove event handler in the MapView component, which calculates the geographic coordinates of the pointer and commits them to the app's Vuex store. The StatusBar component displays the values from the Vuex store.

The problems are twofold.

  1. I am unsure if it's right to be committing to the Vuex store on mousemove. The mousemove event can fire up to 60–100 times per second. Amongst other problems, this causes the Vue devtools plugin to freeze.

  2. The calculation of the geographic coordinates is not completely trivial. It would be better if it was done only when rendering the StatusBar, rather than every time the mousemove event handler runs. But to do that, I would need to be able to access methods of the MapView component from the StatusBar component, which I don't think is possible (or at least idiomatic) in Vue.

I can see a couple of other options, but I'm not sure of the merits of them:

  • I could use a global event bus, and the mousemove event handler in the MapView component could fire an event which included a reference to the MapView as an argument. Then the StatusBar component would have access to the MapView to call it's methods to calculate the geographic coordinates. This would avoid the extremely frequent commits to the Vuex store, but I don't know if it would be any more efficient really. Furthermore, I would still be effectively doing the geographic calculations on mousemove, rather than on render.

  • Perhaps I could store a reference to the MapView as a property of $root, and thus be able to directly access it's methods from the StatusBar component. This feels cludgy and I'm sure it's not an idiomatic thins to do in Vue.

Both of the above methods would also result in tight bindings between the StatusBar and MapView components, which is less than ideal.

  • A third option would be to simply throttle my mousemove handler to run less often. But for the status bar to update smoothly it would still have to run 10 times a second or so, and I'm still uncertain if Vuex is the right place for data which changes that often.

What would be the idiomatic (and most performant) way to handle this in Vue? And is the Vuex store the right place for data which changes so frequently?

Caesar
  • 577
  • 4
  • 15

1 Answers1

1

It seems that you just need to reduce the number of commit calls. Try to use debounce or throttle in the mousemove callback to achieve that instead before refactoring large parts of your code.
What does _.debounce do? - good explanation for debounce.

import {debounce} from 'lodash' 
{
 template :  '<div @mousemove="update"></div>',
 methods :{
   update:  debounce(function(event){
      this.$store.dispatch('updateInput',event)
    }, 2000)
  }
}
elad frizi
  • 655
  • 4
  • 9
  • Yes, I was considering that as an option too; I should have mentioned that above. But I would like the status bar to update reasonably often/smoothly, so still say 10 times a second or more. Obviously that would be a performance improvement over performing the work 60-100 times a second, but I'm still not sure if the Vuex store is the right place for state which updates so often, or if there might be a better alternative way to share that data between the two components. – Caesar Oct 01 '18 at 16:34
  • I've updated my question to mention throttling as an option. Thanks for your answer – it may still turn out to be the best way, but I'm hoping there's something else I haven't thought of! :-) – Caesar Oct 01 '18 at 16:44
  • 10 commits per second is not to much. You dont need to worry about preformence from the vuex side. – elad frizi Oct 01 '18 at 17:00