DEV Community

Roland Doda
Roland Doda

Posted on • Edited on

8 secrets Vue Developers must know


Inline statement handler: Handle multiple arguments

Short Explanation:

In an inline statement handler besides the $event special variable we have access to another special variable: arguments. $event will have access only to the first argument, whilst the arguments will have access to all arguments.

Detailed Explanation:

In an inline statement handler we already know that we have access to the special $event variable.

So if a child components emits an event with a parameter, we have access to it by using $event:

Parent component

<template>
  // ...
   <Child @event="someVariable = $event" />
  // ...
</template>
Enter fullscreen mode Exit fullscreen mode

Child component

export default {
  //...
    emitEvent () {
      this.$emit('event', 'a simple text')
    }
  //...
}
Enter fullscreen mode Exit fullscreen mode

That works really well when the child component, is a component that we have access to, since we can be sure we pass only one parameter.

But how if we use a third-party component/library (e.g. dropzone.js) which passes many arguments up via event ??

The $event will have access only to the first argument. See this codesandbox example which illustrates that $event will catch only the first argument. (Click the button and see the console)

In that case, instead of $event we can use arguments and all arguments will be accessible.

So in the codesandbox above, in order to make it work, we have to change the line 4 in the Child.vue

from:

<GrandChild @event="$emit('event', $event)"/> 
Enter fullscreen mode Exit fullscreen mode

to:

 <GrandChild @event="$emit('event', arguments)"/>
Enter fullscreen mode Exit fullscreen mode

Emit event from functional components

Short Explanation:

Using functional components, means that we don't have access to this context. Thus, we cannot do this.$emit(). But... we have access to listeners so we can do <button @click="listeners.clicked"></button>

Detailed Explanation:

Functional components are stateless (no reactive data) and instanceless (no this context). But functional components have access to some properties such as props, children etc and most importantly (for this case), listeners.

According to Vue docs:
listeners: An object containing parent-registered event listeners. This is an alias to data.on

That means that we can emit event from functional components. Wiii :P

Simple example:

<template functional>
  <button @click="listeners['custom-event']('message from child')">
    Button from child
  </button>
</template>
Enter fullscreen mode Exit fullscreen mode

Working example (click the button and open the console)

How if we want to emit an event from functional component with render functions? Can we do that? Of course!

Simple example:

export default {
  functional: true,
  render(createElement, { listeners }) {
    return createElement(
      "button",
      {
        on: {
          click: event => {
            const emit_event = listeners.event_from_child;
            emit_event("Hello World!Is this the message we excpected? :/");
          }
        }
      },
      "Pass event to parent"
    );
  }
};
Enter fullscreen mode Exit fullscreen mode

Working example

Someone may wonder if we can use .sync Modifier using this approach.
The answer ? Of course!

<button @click="listeners['update:message']('some text')">Click me</button>
Enter fullscreen mode Exit fullscreen mode

Pass all props to the child component

Say that we have a component which receives props and we want to pass all those props to a child component. To achieve that, we can do:

  <ChildComponent v-bind="$props" />
Enter fullscreen mode Exit fullscreen mode

Taking advantage of v-bind we can also have an object like:

data: () =>({
  obj: {
    firstName: 'John',
    lastName: 'Doe',
    age: 30
  }
})
Enter fullscreen mode Exit fullscreen mode

And pass firstName, lastName, age as props to a child component, like:

<Child v-bind="obj" />
Enter fullscreen mode Exit fullscreen mode

Watch child properties changes from parent

You may wonder why to do that and you are right! That's a bad practice. But sometimes you use a third-party component and you want to watch their properties to fit your needs.

A long time ago, in a project we were using a date picker and we wanted to be able to detect when the popup was visible. Unfortunately there was no option to do that.I discovered that the date picker was using a popupVisible reactive property but it wasn't exposed by the library. So I had to somehow watch this property from my component.

The below code is NOT able to detect changes:

  watch: {
    '$refs.datePicker.popupVisible': {
      handler (new_value) {
        console.log('value changed')
      },
      deep: true
    }
  }
Enter fullscreen mode Exit fullscreen mode

In order to detect changes of a child component's property you should do:

  mounted() {
    this.$watch(
      "$refs.picker.popupVisible",
      (new_value, old_value) => {
         //execute your code here
      }
    );
  }
Enter fullscreen mode Exit fullscreen mode

Working example:

Learn more about vm.$watch


Listening for child event in the router view

Most of you, should already know that since it is straightforward, but I've been asked many times about the question below.

Say you have a component which has nested routes:

<template>
  //...
  <router-view></router-view>
  //...
</template>
Enter fullscreen mode Exit fullscreen mode

And you have a nested route as follow:

<template>
  //...
  <button @click="$emit('event')"></button>
  //...
</template>
Enter fullscreen mode Exit fullscreen mode

So the component corresponding to a nested route, emits an event. The question is : how to listen to that event ?

The simple answer demonstrated with code:

<template>
  //...
  <router-view @event="callAMethod"></router-view>
  //...
</template>
Enter fullscreen mode Exit fullscreen mode

Exactly! We listen to that event in the router-view component


Vue components lifecycle hooks don't run in the order you think they run

Say you have 2 pages. Home and About.

When switching from Home to About, the created hook of the About component will run before the beforeDestroy hook of Home component. (take a moment here)

Weird?? Try switching routes in the working example below by seeing the console.

As solution (idk if it's the best solution though) you can use transition-mode out-in

    <transition mode="out-in">
      <router-view></router-view>
    </transition>
Enter fullscreen mode Exit fullscreen mode

How to know if a child component is mounted ?

This one of my favorite tips I've read here (Vue Dose)

Say you have a child component and you want to do something when a hook of the child component is executed. You can do:

<Child @hook:created="doSomething" />
Enter fullscreen mode Exit fullscreen mode

So the question to How to know when a child component mounted is:

<Child @hook:mounted="componentMountedDoSomething" />
Enter fullscreen mode Exit fullscreen mode

How to know if a dynamically vuex registered module is registered ?

With the power of dynamically register/unregister vuex modules we can improve the performance a lot.

I recommend you to read a very useful article: Performance optimization: Lazy load vuex modules

We can register a vuex module:

this.$store.registerModule('my-module', MyModule)

And unregister it:

this.$store.unregisterModule('my-module')

To know if a module is already registered:

if (Object.keys(this.$store._modules.root._children).includes('my-module')) {
        // module is registered
}
Enter fullscreen mode Exit fullscreen mode

My time is very limited but I am glad that I found some time to write this article (it took me some time though). I hope it helped and you enjoyed reading it.

I would be very happy if you leave a comment providing your solution. For example regarding the last "secret" you can use vuex store to indicate that a module is registered or not.

This is my first article on DEV community. Thank you for reading.

Top comments (10)

Collapse
 
anduser96 profile image
Andrei Gatej

Really great tips! Thanks for sharing!

Hereโ€™s my little tip, how to add multiple listeners to a functional component.

// in render function

const functionNames = Object.keys(listeners);
const combinedFunctions = ctx => functionNames.forEach(fn => listeners[fn](ctx));

 const button = h('button', {
     ...functionNames.length && { on: { click: combinedFunctions } } 
   }, content);

return button;
Collapse
 
the_one profile image
Roland Doda

Thanks for the comment Andrei. Is that equal to:

<button v-on="listeners"> Click me </button>

?

Collapse
 
vhoyer profile image
Vinรญcius Hoyer

why is that listeners instead of $listeners?

Thread Thread
 
the_one profile image
Roland Doda

in functional components, it's listeners. Docs

Collapse
 
anduser96 profile image
Andrei Gatej

Doesnโ€™t seem to work. ๐Ÿค”

Thread Thread
 
the_one profile image
Roland Doda

What doesn't seem to work?

Thread Thread
 
anduser96 profile image
Andrei Gatej

Your snippet. Thereโ€™s a likelihood of me doing something wrong though. Iโ€™m rendering my component using a render function.

Thread Thread
 
the_one profile image
Roland Doda

Since we are talking about functional components the full code snippet should be:

<template functional>
  <button v-on="listeners">Click me </button>
</template>
Thread Thread
 
anduser96 profile image
Andrei Gatej

Iโ€™ll give it another try tomorrow and Iโ€™ll let you know how it went!

Collapse
 
avxkim profile image
Alexander Kim

In this example:

export default {
  functional: true,
  render(createElement, { listeners }) {
    return createElement(
      "button",
      {
        on: {
          click: event => {
            const emit_event = listeners.event_from_child;
            emit_event("Hello World!Is this the message we excpected? :/");
          }
        }
      },
      "Pass event to parent"
    );
  }
};

Don't forget to add condition to check, if emit actually requested from a functional component:

if (emit_event) emit_event("Hello World!Is this the message we excpected? :/");

Otherwise, if you won't listen to that emitted event, then you'll get an error.