8
votes

When my mouse cursor enters and leaves my VueJS component, the following methods are called respectively.

Methods that are called when the cursor enters and leaves my component:

// located in "methods" section of my Vue component file

onMouseEnter() {
    window.Event.$emit("mouse-entered", this.index);
    console.log("Mouse entered");
},
onMouseLeave() {
    window.Event.$emit("mouse-left", this.index);
    console.log("Mouse left");
},

Expectedly, this is what my console looks like when my cursor enters and leaves the component (one event is emitted each time):

enter image description here

However, what's really strange is that in Vue dev tools, I'm seeing duplicate events being emitted every time my cursor enters and leaves my component:

enter image description here

Given this conflicting information, I'm not sure what to believe. Refreshing the page sometimes eliminates the duplicate events in dev tools but I always get single, unique events in my console, which is my desired behaviour.

Does anyone know what is happening here and what should I accept as the source of truth?

Below is how my Vue instances are being declared and initialised in my main.js file:

// As far as I can tell, duplicated Vue instances are not being created here on page refresh

let app;

// global event bus
window.Event = new Vue();
console.log("Event bus created");

/* This section ensures the firebase auth object isn't in an intermediary state. */
firebase.auth().onAuthStateChanged(() => {
    if (!app) {
        console.log("New Vue instance created");
        app = new Vue({
            el: '#app',
            router,
            store,
            render: h => h(App)
        });
    }
});

Note that this particular component is being reused on two different routes( "dashboard" and "home" ), which are both being kept alive by the following code.

// Template section of App.vue file
<template>
    <div id="app">
        <keep-alive
            v-bind:include="[ 'dashboard', 'home' ]"
            <router-view></router-view>
        </keep-alive>
    </div>
</template>

Additionally, since those two routes are being kept alive and cached, failure to turn $off event emitters and listeners should not be the cause of duplication (at least I think it shouldn't).

EDIT 1: I did a find all for "mouse-entered" and "mouse-left" in every single directory in my project and I can confirm that both events are only emitted from the Vue component I've referenced to in this post.

EDIT 2: To help with debugging, I put a listener on my top-most component (App.vue) to see if it receives the event twice (refer to the created hook in the code snippet below). I can confirm it also only receives the event once. I've also pasted in my complete App.vue file since the above example was mainly to illustrate that I'm keeping alive "dashboard" and "home".

<template>
    <div id="app">
        <keep-alive
            v-bind:include="keepAlive">
            <router-view></router-view>
        </keep-alive>
    </div>
</template>

<script>
    export default {
        name: 'app',
        data() {
            return {
                isLoggedIn: false,
            };
        },
        computed: {
            keepAlive() {
                if (this.isLoggedIn) {
                    return [ 'dashboard', 'home', 'results' ];
                }
                return [ 'landing', 'signIn', 'signUp' ];
            }
        },
        watch: {
            /* This watches for route changes */
            $route(to, from) {
                /* firebase.auth().currentUser returns null if user has logged out */
                if (firebase.auth().currentUser) {
                    this.isLoggedIn = true;
                } else {
                    this.isLoggedIn = false;
                }
            }
        },
        created() {
            window.Event.$on("mouse-entered", () => {
                console.log("mouse-entered-from-App.vue");
            });
            window.Event.$on("mouse-left", () => {
                console.log("mouse-left-from-App.vue");
            });
        }
    };
</script>

As expected, App.vue receives the event once (see below); however, I'm still getting duplicate events in Vue dev tools :(

enter image description here

1
That's definitely odd. Perhaps unnecessary, but have you double-checked that you don't have any mouse-entered events somewhere else in your code without a console.log that could be triggered somehow? Are you able to consistently get this behaviour by navigating? Or is editing and saving your code involved?Sumurai8
@Sumurai8 I can confirm that the events are never emitted from other components after doing a find all in my project's directories. It consistently happens when I navigate to other routes and back to the routes (dashboard and home) that this component resides on. Also the events only ever duplicate, never more.p4t
I can't really think of a situation where the event would fire, but the console message would not be duplicated, so you could try to put a listener on your top-most component and see if that receives the event twice. If it doesn't, I think it is safe to say that the vue devtools have a bug. Otherwise we need to look further.Sumurai8
@Sumurai8 I've put a listener on App.vue and I can confirm that it only receives the event once but I still get duplicate events emitted in dev tools. I've also added my entire App.vue code, which looks ok to me but might reveal something to others.p4t
as long as the event is effectively only triggered once (e.g. console logging) it shouldn't matter what is displayed in the dev tools, your program should work. doesn't it? .. this might be a bug in the vue-devtools. some hidden event bubbling maybe?Tobias Würth

1 Answers

6
votes

You wrote

Refreshing the page sometimes eliminates the duplicate events in dev tools but I always get single, unique events in my console, which is my desired behaviour.

The way you describe your problem, the events are emitted correctly, while they are being reacted on in a wrong, duplicate way. I think what you might be missing is unsubscribing from your event bus once the component gets destroyed. You should use the beforeDestroy hook to do so (similar to the way you used created earlier on in the component's lifecycle to subscribe).

Something like this:

beforeDestroy() {
    window.Event.$off('mouse-entered');
    window.Event.$off('mouse-left');
}