Join 11,067 other Vue devs and get exclusive tips and insights delivered straight to your inbox, every week.
👋Hey friend! I work hard to send you amazing stuff each week.
— Michael
Hey all,
I hope you're doing well! This week I'm enjoying some summer vacation.
But I do have some tips for you, as always.
Also, don't forget that Nuxt Tips Collection is coming soon, on August 5th! You can sign up here for a special launch day discount if you're interested.
I've already been getting some great feedback from my beta testers, and Alex Lichter (part of the Nuxt team) is doing a technical review of all the tips.
Enjoy the tips, and enjoy your week!
— Michael
Callback is a bit of a dirty word in Vue, but there are great use cases for them.
Consider a scenario where a parent component needs to react to changes in its children's state. You can use a reactive object provided by the parent and injected by the children to keep track of changes.
Here's an example:
// Parent componentconst sharedState = reactive({});provide('sharedState', sharedState);// Child componentconst sharedState = inject('sharedState');
When a child component updates a property of sharedState
, Vue's reactivity system ensures that any effects or computed properties that depend on sharedState
are automatically re-evaluated.
You can use callbacks to allow child components to register themselves with the parent or to signal the parent to perform an action.
Here's how you might implement a callback with provide
and inject
:
// Parent componentconst registerChild = (child) => { /* ... */ };provide('registerChild', registerChild);// Child componentconst registerChild = inject('registerChild');registerChild(/* child details */);
This pattern is powerful for creating complex interactions between components while keeping the logic encapsulated and manageable.
It's especially useful in the Compound Components pattern.
If you ever need to implement dragging or to move something along with the mouse, here's how you do it:
requestAnimationFrame
. Lodash's throttle
method with no wait
parameter will do this. If you don't throttle, your event will fire faster than the screen can even refresh, and you'll waste CPU cycles and the
smoothness of the movement.Here's a basic example of tracking mouse movements using the Composition API. I didn't include throttling in order to keep things clearer:
// In your setup() functionwindow.addEventListener("mousemove", (e) => {// Only move the element when we're holding down the mouseif (dragging.value) {// Calculate how far the mouse moved since the last// time we checkedconst diffX = e.clientX - mouseX.value;const diffY = e.clientY - mouseY.value;// Move the element exactly how far the mouse movedx.value += diffX;y.value += diffY;}// Always keep track of where the mouse ismouseX.value = e.clientX;mouseY.value = e.clientY;});
Here's the full example. You can check out a working demo here:
<template><div class="drag-container"><imgalt="Vue logo"src="./assets/logo.png":style="{left: `${x}px`,top: `${y}px`,cursor: dragging ? 'grabbing' : 'grab',}"draggable="false"@mousedown="dragging = true"/></div></template>
<script setup>import { ref } from "vue";const dragging = ref(false);const mouseX = ref(0);const mouseY = ref(0);const x = ref(100);const y = ref(100);window.addEventListener("mousemove", (e) => {if (dragging.value) {const diffX = e.clientX - mouseX.value;const diffY = e.clientY - mouseY.value;x.value += diffX;y.value += diffY;}mouseX.value = e.clientX;mouseY.value = e.clientY;});window.addEventListener("mouseup", () => {dragging.value = false;});</script>
Aria roles are used to tell a screenreader what an element is for.
This is really important when the native HTML element just doesn't exist (eg. roles like toolbar
and alert
) or when you're using a different HTML element for design or technical reasons (eg. wrapping a radio
button to style it).
But please, remember that you should always use the semantic element where you can. This is always the best and most effective solution.
There are six different categories of aria roles:
button
, checkbox
, separator
, tab
, or scrollbar
combobox
and listbox
(these are for dropdown menus), radiogroup
, or tree
article
, presentation
, figure
, feed
, and directory
banner
, main
, navigation
, and region
are roles in this categoryalert
, log
, marquee
, and status
are roles that might update with real-time informationalertdialog
and dialog
are the only two roles in this categoryYou can check out the full list here: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#roles
In this episode of DejaVue, Alex and Michael are joined by Vanessa Otto, a Senior Engineer at GitLab and previously the head of Frontend at Zavvy.
After discussing Vanessa's co-host role in the German Working Draft podcast, it all revolves around the headless editor "Tiptap". From hearing why Vanessa chose it instead of other editors and her research around editors, Tiptap's integration with Vue, and what the "headless" part of the editor adds to it, over to which use cases are ideal for Tiptap.
And yes, an example repository is included so you can convince yourself of the easiness! Enjoy the episode!
Watch on YouTube or listen on your favourite podcast platform.
Chapters:
In case you missed them:
This article is a great look at how you can use the flush
option with watchers to create some pretty complex and useful behaviours.
I don't want to spoil it for you though. Just read the article!
Check it out here: Ignorable Watch
I teamed up with Vue Mastery to create this series on coding better composables.
In this series we cover five different patterns.
For each, we show how you can implement it and then we see it in action with a composable from VueUse.
This fifth and final article of the series shows how we can use the Async Without Await pattern to simplify our composables.
Check it out here: Coding Better Composables: Options Object (5/5)
Here are some upcoming events you might be interested in. Let me know if I've missed any!
The first Czech Vue.js conference, taking place in Cinema City - Nový Smíchov
A community-driven Vue conference in Germany. Listen to great talks from great speakers and meet the wonderful VueJS Community.
My favourite Vue conference, in my own backyard! A three-day event with workshops, speakers from around the world, and socializing.
"Things aren’t always #000000 and #FFFFFF." — undefined
The best way to commit something to long-term memory is to periodically review it, gradually increasing the time between reviews 👨🔬
Actually remembering these tips is much more useful than just a quick distraction, so here's a tip from a couple weeks ago to jog your memory.
Testing is important to do, but it can be hard to do.
In my experience, good architecture lends itself to easy-to-write tests (or at least, easier-to-write). The inverse is also true, that difficult-to-write tests are typically a symptom of poor architecture.
Of course, sometimes tests are just hard to write, and there’s no way around it.
The best thing we can do is borrow a tool from mathematics and science, and transform a difficult problem into an easier but equivalent one:
Michael Hoffman curates a fantastic weekly newsletter with the best Vue and Nuxt links.
p.s. I also have four products/courses: Clean Components Toolkit, Vue Tips Collection 2, Mastering Nuxt 3, and Reusable Components