What’s the best way to “extend” a Vue component, i.e. use one component as the basis for other components?
Doing this could save you from duplicating code, making your components quicker to develop and easier to maintain.
There are a number of APIs and patterns that Vue offers for this, and you’ll need to choose the right one depending both on your goals and personal taste.
In this article, I’ll give you an overview of the different options to help you choose the best and most suitable for your scenario.
Keep in mind that all methods of extending components can add complexity and verbosity to your code, and in some cases, additional performance overhead.
So before you decide to extend a component, it’s a good idea to first check if there are simpler design patterns that can achieve what you want.
The following component design patterns can often be sufficient substitutes for extending a component:
Let’s do our due diligence by briefly reviewing these.
The easiest way to make a component multi-use, and thus avoid extending it, is to provide a prop which drives conditional logic in the template.
In the following example, we use a prop type
for this purpose.
<template>
<div class="wrapper">
<div v-if="type === 'a'">...</div>
<div v-else-if="type === 'b'">...</div>
<!--etc etc-->
</div>
</template>
<script>
export default {
props: { type: String },
...
}
</script>
A parent can then declare this component and use the prop to get the variation required.
<template>
<MyVersatileComponent type="a" />
<MyVersatileComponent type="b" />
</template>
Here are two indicators that you’ve either hit the limits of this pattern or are misusing it:
Another way to make a component versatile without extending it is to allow the parent component to set custom content within the child using slots.
<template>
<div class="wrapper">
<h3>Common markup</div>
<slot />
</div>
</template>
<template>
<MyVersatileComponent>
<h4>Inserting into the slot</h4>
</MyVersatileComponent>
</template>
Renders as:
<div class="wrapper">
<h3>Common markup</div>
<h4>Inserting into the slot</h4>
</div>
A potential limitation of this pattern is that the elements in the slot belong to the parent’s context, which may not be a natural way of dividing your logic and state.
Scoped slots can bring more flexibility, and we’ll explore these more in the section on renderless components.
If you only need to reuse standalone functions across your components, you can simply extract these into JavaScript modules without any need to use an extending pattern.
JavaScript’s module system is a very flexible and robust way of sharing code, so you should lean on it where possible.
export default function () {
...
}
import MyUtilityFunction from "./MyUtilityFunction";
export default {
methods: {
MyUtilityFunction
}
}
#vue.js #components #composition api #renderless components #vue