Vue.js — Considerations and Tricks

Harshal Patil
webf
Published in
7 min readMay 18, 2018

--

Vue.js is great. However, when you start building large scale JavaScript applications, you will start to hit the boundaries of the Vue.js. These boundaries are not really the limitations of the framework; rather these are the important design decisions that Vue.js team had taken from time to time.

Unlike React or Angular, Vue.js caters to different level of developers. It is friendly, easy-to-use for beginners and equally flexible for experts. It doesn’t try to shy away from DOM. Instead, it plays well with it.

Having said this, this article is more like a catalog of some of the great discussions, issues and tricks I have come across on my way to Vue.js enlightenment. Understanding these key design aspects helped us while building our large scale web applications.

Again, these discussion are valid as of today, May 18th, 2018. When framework upgrades, underlying browser and JS API will change, they may not be valid and intuitive.

1. Why Vue.js doesn’t use ES classes out-of-box for component?

If you are coming from Angular-like framework or some backend OOP heavy language, your first question will be — why not classes for components?

Vue.js creator, Evan You, has nicely answered this question in this GitHub comment:

There are three main reason why not classes as default mechanism:

  1. ES Classes are not good enough to meet the needs of Vue.js current API. ES Classes are not fully evolved and often criticized as a step in wrong direction. Classes with private fields and decorators once stabilized (Stage 3 at least) may help.
  2. ES Classes serve good only those who are familiar with Class based languages. It easily excludes large community of web that doesn’t use sophisticated build tools or transpilers.
  3. Building great UI component hierarchy is about great component composition. It is not about great inheritance hierarchy. Unfortunately, ES classes are better at the latter.

2. How can I build my own abstract component?

If building large scale web application is not enough, you have some crazy idea to implement an abstract component like <transition> or <router-view>. There was definitely a discussion about this, but it really did not sail through.

But fear not, with good understanding of slots, you can build your own abstract components. There is a very good blog post explaining how to do that.

But still think twice before you do that. We have always relied on mixins and plain functions to solve some corner case scenarios. Just think of mixins as your abstract components:

3. I am not really comfortable with Vue.js Single File Component Approach. I am more happy with Separate HTML, CSS and JavaScript.

Nobody has stopped you from doing so. If you are old school Separation of Concern philosopher who like to literally put separate things into separate files or you hate code editor’s erratic behavior around .vue files, then it is certainly possible. All you have to do:

<!--https://vuejs.org/v2/guide/single-file-components.html --><!-- my-component.vue -->
<template src="./my-component.html"></template>
<script src="./my-component.js"></script>
<style src="./my-component.css"></style>

However, immediately next question comes up — Do I always need 4 files (vue + html + js + css) for my component. Can I somehow get rid of .vue files? Answer is definitely yes, you can do that. Use vue-template-loader

My colleguage has written a great post about it:

4. Functional Components

Thanks to React.js, functional components are craze now a days albeit for a good reason. They are fast, stateless and easy to test. However, there are gotchas.

4.1 Why can’t I use class based @Component decorator for Functional Components?

Again coming back to classes, it should be noted that classes are a data structures that are meant to hold local state. If functional components are stateless, then is there really no point inhaving @Component decorator.

The relevant discussion is available at:

4.2 External classes and styles are not applied to Functional components

Functional components do not have class and style binding like normal components. One has to manually apply these binding within render function

4.3 Functional components are always re-rendered?

TLDR: Be careful when using statuful components inside functional components

Functional components are eager meaning render function of the component gets directly called. This also means that you should:

Avoid using stateful component directly within render function because that will create different component definition on every call to render function.

Functional component are better used if they are leaf components. It should be noted that this same behavior also applies to React.js.

4.4 How to emit an event from Vue.js Functional component?

Emitting an event from Functional component is not straight forward. Unfortunately, there is nothing mentioned in the docs about this. $emit method is not available within functional component. Following stack overflow question will help in this regard:

5. Vue.js Transparent wrapper components

Transparent wrapper component wraps some DOM structure and yet expose events for that DOM structure instead of root DOM element. For example,

<!-- Wrapper component for input -->
<template>
<div class="wrapper-comp">
<label>My Label</label>
<input @focus="$emit('focus')" type="text"/>
</div>
</template>

Here we are really interested in input tag and not with root div element as it is mostly added for styling and cosmetic purpose. The user of this component might be interested in several events from input like blur, focus, click, hover, etc. It means we have to re-emit each event. Our component would look like this.

<!-- Wrapper component for input -->
<template>
<div class="wrapper-comp">
<label>My Label</label>
<input type="text"
@focus="$emit('focus')"
@click="$emit('click')"
@blur="$emit('blur')"
@hover="$emit('hover')"
/>
</div>
</template>

Now that is anti-DRY and looks messy. Simple solution is to simply rebind your event listeners to required DOM elements using vm.$listeners property on Vue instance:

<!-- Notice the use of $listeners -->
<template>
<div class="wrapper-comp">
<label>My Label</label>
<input v-on="$listeners" type="text"/>
</div>
</template>
<!-- Uses: @focus event will bind to internal input element -->
<custom-input @focus="onFocus"></custom-input>

6. Why you cannot v-on or emit from the slot

I have often seen developers trying to emit event from a slot or to listen for events on a slot. That is simply not possible.

Component slot is provided by calling/parent component. It means all the events should associated with the calling component. Trying to listen for those changes means your parent and child component are tightly coupled and there is another route to do that which is explainly beautifully by Evan You:

7. Slot within a slot (Read grandchildren slot)

At some point, you will encounter this scenario. Imagine you have a component, say A, that accepts some slots. Following the principles of composition, you build another component B using component A. Now you take component B and use it in component C.

Question is — How do you pass slot from component C to component A?

Answer to this question depends on what you use? If you use render function, then it is pretty trivial. Render function of component B will be:

// Render function for component B
function render(h) {
return h('component-a', {
// Passing slots as they are to component A
scopedSlot: this.$scopedSlots
}
}

However, if you use template based render function, then you are out of luck. Fortunately, there is a progress happening on this issue and we may have something for template based components:

Hopefully, this post has given in insights into Vue.js design considerations and some tips/tricks to play with advanced scenarios in Vue.js.

--

--

User Interfaces, Fanatic Functional, Writer and Obsessed with Readable Code, In love with ML and LISP… but writing JavaScript day-in-day-out.