loading...

8 secrets Vue Developers must know

rolanddoda profile image Roland Doda Updated on ・5 min read

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>

Child component

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

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)"/> 

to:

 <GrandChild @event="$emit('event', arguments)"/>

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>

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

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>

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

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

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

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

<Child v-bind="obj" />

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

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

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>

And you have a nested route as follow:

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

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>

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>

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

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

<Child @hook:mounted="componentMountedDoSomething" />

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
}

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.

Discussion

pic
Editor guide
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
rolanddoda profile image
Roland Doda Author

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
rolanddoda profile image
Roland Doda Author

in functional components, it's listeners. Docs

Collapse
anduser96 profile image
Andrei Gatej

Doesn’t seem to work. 🤔

Thread Thread
rolanddoda profile image
Roland Doda Author

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
rolanddoda profile image
Roland Doda Author

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