Would you like to buy me a ☕️ instead?
On my journey to find ways to improve the rendering performance of large scale Vue.js applications, I stumble upon functional components as a possible solution from time to time. But so far, I’ve always found one or two reasons why I can’t use them in my application.
Table of Contents
Special characteristics of functional components
Functional components have some restrictions that cause them to behave differently than other components. For example, attributes are not passed along automatically, i.e., classes or ID’s specified on a functional component are ignored by default.
<!-- src/components/ArticleTeaser.vue -->
<template>
<div class="ArticleTeaser">
<UiHeadline
id="hyphenCase(article.title)"
class="ArticleTeaser__title"
@click="readMore"
>
{{ article.title }}
</UiHeadline>
<p>{{ article.intro }}</p>
</div>
</template>
<!-- src/components/UiHeadline.vue -->
<template functional>
<h1>
<slot/>
</h1>
</template>
In the above example, none of the attributes would work on the template-based functional UiHeadline
component. There would be no id
or class
, and also the @click
event handler would not be fired.
The biggest problem with that, in my opinion, is that this is not transparent in any way. Imagine that the person who created the UiHeadline
component is not the same person who is responsible for the ArticleTeaser
component - developers who only use the UiHeadline
component might have no idea why these attributes don’t work, at least not until they take a closer look at the source code.
But fortunately, there are ways to solve this, and it is the responsibility of the developer who builds the functional component to make it behave like a normal component.
Passing along attributes and event listeners
Let’s start with making it possible to pass along attributes and event listeners just as with regular components.
<template functional>
<h1
v-bind="data.attrs"
v-on="listeners"
>
<slot/>
</h1>
</template>
Here you can see that with v-bind
we can pass on all HTML attributes and we can use v-on
to do the same with event listeners.
Although you may think that this should do the trick, we are not quite finished yet, because unfortunately the class
attribute is not part of data.attrs
.
Do you want to learn more about advanced Vue.js techniques?
Register for the Newsletter of my upcoming book: Advanced Vue.js Application Architecture.
Passing classes, styles and refs to functional components
Because Vue.js applies a special logic to the class
, style
and ref
attributes, they are not part of data.attrs
, but instead you can access classes, styles and refs via data.class
/ data.staticClass
, data.style
/ data.staticStyle
and data.ref
.
<!-- Goes into `data.class` -->
<UiHeadline :class="['my-class']"/>
<!-- Goes into `data.staticClass` -->
<UiHeadline class="my-class"/>
This means that if you want to make your template-based functional components fully transparent, you have to apply data.class
and data.staticClass
as well as data.style
and data.staticStyle
and also data.ref
manually.
<template functional>
<h1
:ref="data.ref"
:class="[data.class, data.staticClass]"
:style="[data.style, data.staticStyle]"
v-bind="data.attrs"
v-on="listeners"
>
<slot/>
</h1>
</template>
Now our functional UiHeadline
component is fully transparent and it doesn’t matter to the people who use it that they’re actually dealing with a functional component. Many thanks to Nico Prat, who made me aware that the style
and ref
attributes must also be taken into account.
Benchmarks
You may be wondering why you should go the extra mile using functional components when you have to do all this extra work to use them. First, there are situations where you may not need to pass on attributes and event listeners, and second, functional components are usually much faster than normal components. Take a look at the following benchmark performed by Austin Gil to see the potential performance benefits.
Wrapping it up
If you plan to use template-based functional components, I highly recommend that you pass on all attributes, classes, and event listeners to avoid confusion for those who need to use these components as building blocks in their own components.
Apart from these caveats, refactoring parts of your code base to use of functional components can be a great way to improve the performance of your Vue.js powered application.