Vue is amazing. Seriously, Evan You and the community have done an amazing job with it and I've spent a good part of my career working with Vue now. It kind of has become my go-to framework over the years and I would absolutely recommend it to everyone.
Since I love to share my knowledge, in this article I want to show you 5 tips I wish I knew when I started out with Vue.
If you want even more tips, I recently got the opportunity to write a guest post on the company blog of UPDIVISION and I wrote up 10 more tips! UPDIVISION is an international tech company that specialized on all things web and I want to thank them for the opportunity to share my knowledge with even more people!
So, let's get started with the tips, shall we?
1. Vue isn’t just for single page apps
There's a lot of tutorials out there that show you how to build things with Nuxt (basically Vues version of Next.js) - but Vue is pretty flexible when it comes to where and how it's being used.
I worked on several Drupal projects lately and Drupal is notorious for using jQuery and Twig in their rendering chain, but using Vue every now and then would make some things so much simpler!
Well, you can, actually. Using several Vue apps on the same page is very well possible. They can even share Vuex stores, components and instances of third party libraries.
Let's make a quick example. Let's assume you've got the following HTML setup coming from some serverside framework, like Drupal or Laravel:
<!DOCTYPE html>
<html>
<head>
<!-- ... -->
</head>
<body>
<div id="app-header"></div>
<main>
<!-- PHP/Python/Java can happen here -->
</main>
<div id="app-footer"></div>
</body>
</html>
You can now render the header and the footer as two different Vue apps:
import Vue from 'vue'
// The Vuex store, will be shared
import Store from '@/store/myVuexStore'
// The two components
import Navigation from '@/components/Navigation.vue'
import Footer from '@/components/Footer.vue'
// Header app
new Vue({
Store,
render: (h) => h(Navigation)
}).$mount('#app-header')
// Footer app
new Vue({
Store,
render: (h) => h(Footer)
}).$mount('#app-footer')
2. There's a shorthand syntax to bind many attributes at once
Perhaps you've created a component that has a lot of props. Most of them are actually used by some child component, though, but binding all of them would be tedious.
You can bind all the props that your component got to any child component by using v-bind
with an object:
<template>
<div>
<div>
<div>
<ChildComponent v-bind="$attrs" />
</div>
</div>
</div>
</template>
$attrs
is simply all props that the current component got passed.
3. Slots help you to don't repeat yourself
Imagine you've got a a container element you're repeating all the time. If you use Tailwind, this might happen even faster.
Let's look at this example of a landing page :
<template>
<section class="mb-5 px-12 py-6 bg-brand-light" id="section-1">
<h2>Some testimonial</h2>
<!-- ... -->
</section>
<section class="mb-5 px-12 py-6 bg-brand-light" id="section-2">
<h2>Some diagrams</h2>
<!-- ... -->
</section>
<section class="mb-5 px-12 py-6 bg-brand-light" id="section-3">
<h2>Some more testimonials</h2>
<!-- ... -->
</section>
<section class="mb-5 px-12 py-6 bg-brand-light" id="section-4">
<h2>Some contact info</h2>
<!-- ... -->
</section>
</template>
Now that's a lot of repetition, right? Let's abstract the section away. We can create a new component called a landing-section
that only has the following template:
<template>
<section class="mb-5 px-12 py-6 bg-brand-light">
<slot></slot>
</section>
</template>
In a nutshell, a slot allows this component to have children. With the placement of the <slot>
we tell Vue where to render the children. Our new landing-section
component can be used on the landing page like this:
<template>
<landing-section>
<h2>Some testimonial</h2>
<!-- ... -->
</landing-section>
<landing-section>
<h2>Some diagrams</h2>
<!-- ... -->
</landing-section>
<landing-section>
<h2>Some more testimonials</h2>
<!-- ... -->
</landing-section>
<landing-section>
<h2>Some contact info</h2>
<!-- ... -->
</landing-section>
</template>
Much cleaner!
4. Nothing stops you from using Vanilla JS every now and then - but be aware of it!
There's certain things that are a lot easier when done in simple Vanilla JS. You can always add event listeners to the window in Vue's life cycle hooks (mounted
, beforeDestroy
etc.) - but remember to clean up after yourself.
If you add event listeners, they won't be detached when the component that introduced them is unmounted, unless you explicitly remove them. If you don't, you run the risk of attaching the same event listener several times, potentially introducing unwanted behaviour and perhaps even some very bad memory leaks.
This is not only relevant for event listeners. Timeouts and intervals need to be cleared as well and if you've got a loop that uses window.requestAnimationFrame
, remember to add a possibility to cancel that, too. Believe me, I've ran into this more often than not and it's really hard to debug.
5. Named slots make DRY components a lot more flexible
A teaser component is probably one of the most created components there is. A teaser usually has a title, a short lead, perhaps it has an image and it surely leads to some URL, right? Perhaps there's one or even more buttons, maybe a few badges as well...
Let's look at an example where we would use props for this use case:
<Teaser
title="Hello, World!"
url="https://www.example.com/"
image="https://via.placeholder.com/450x450"
image-alt="Some alt text"
lead="This is a teaser"
:has-foo-badge="false"
:has-bar-badge="true"
release-date="2022-01-01"
:has-main-button="true"
main-button-text="This is a main button"
/>
Ugh. If there's already that many props, I could imagine what the component looks like: Lots of v-if
s all over the place. The more badges we add, the more buttons we add, the more props there would be and the more complex the template gets, right? It probably looks something like this:
<template>
<div class="teaser">
<img :src="image" :alt="imageAlt" />
<p class="title">
<a :href="url">
{{ title }} {{ releaseDate }}
</a>
</p>
<div class="badges">
<span v-if="hasFooBadge" class="badge">
Foo
</span>
<span v-if="hasBarBadge" class="badge">
Bar
</span>
</div>
{{ lead }}
<div class="buttons">
<button v-if="hasMainButton" class="main-button">
{{ mainButtonText }}
</button>
<button v-if="hasSecondaryButton" class="secondary-button">
{{ secondaryButtonText }}
</button>
</div>
</div>
</template>
We can use named slots instead to tidy things up! A named slot is introduced by giving a <slot>
the attribute name
, like so:
<template>
<div class="teaser">
<slot name="image"></slot>
<p class="title">
<a :href="url">
{{ title }} {{ releaseDate }}
</a>
</p>
<div class="badges">
<slot name="badges"></slot>
</div>
{{ lead }}
<div class="buttons">
<slot name="buttons"></slot>
</div>
</div>
</template>
A named slot works the same way like an unnamed slut, except that there can be multiple ones! Let's rebuild the example with the gazillion props from above:
<Teaser
title="Hello, World!"
release-date="2022-01-01"
lead="This is a teaser"
>
<template v-slot:image>
<img src="https://via.placeholder.com/450x450" alt="Some alt text">
</slot>
<template v-slot:badges>
<span class="badge">
Foo
</span>
<!-- bar-badge missing on purpose -->
</template>
<template v-slot:buttons>
<button class="main-button">
This is a main button
</button>
</template>
</Teaser>
That's a bit more code, but it's a lot more readable and more maintainable!
If you liked these 5 tips and found them helpful, make sure to check out the guest post I wrote over at UPDIVISION that contains 10 more tips - thanks again to these awesome folks the opportunity!
I hope you enjoyed reading this article as much as I enjoyed writing it! If so, leave a ❤️ or a 🦄! I write tech articles in my free time and like to drink coffee every once in a while.
If you want to support my efforts, you can offer me a coffee ☕ or follow me on Twitter 🐦! You can also support me directly via Paypal!
Top comments (0)