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 🔥 Reactivity and Callbacks 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 component const sharedState = reactive({}); provide('sharedState', sharedState); // Child component const 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 component const registerChild = (child) => { /* ... */ }; provide('registerChild', registerChild); // Child component const 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. 🔥 Smooth dragging (and other mouse movements) If you ever need to implement dragging or to move something along with the mouse, here's how you do it: - Always throttle your mouse events using
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. - Don't use absolute values of the mouse position. Instead, you should check how far the mouse has moved between frames. This is a more reliable and smoother method. If you use absolute values, the element's top-left corner will jump to where the mouse is when you first start dragging. Not a great UX if you grab the element from the middle.
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() function window.addEventListener("mousemove", (e) => { // Only move the element when we're holding down the mouse if (dragging.value) { // Calculate how far the mouse moved since the last // time we checked const diffX = e.clientX - mouseX.value; const diffY = e.clientY - mouseY.value; // Move the element exactly how far the mouse moved x.value += diffX; y.value += diffY; } // Always keep track of where the mouse is mouseX.value = e.clientX; mouseY.value = e.clientY; });
<template> <div class="drag-container"> <img alt="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 you didn't know you needed 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: - Widget - roles like
button , checkbox , separator , tab , or scrollbar - Composite - roles like
combobox and listbox (these are for dropdown menus), radiogroup , or tree - Document structure - this includes
article , presentation , figure , feed , and directory - Landmark -
banner , main , navigation , and region are roles in this category - Live region -
alert , log , marquee , and status are roles that might update with real-time information - Window -
alertdialog and dialog are the only two roles in this category You can check out the full list here. 🎙️ #017 — Tiptap and Vue.js (with Vanessa Otto) 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: 📜 Ignorable Watch 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 📜 Coding Better Composables: Options Object (5/5) 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) 📅 Upcoming Events Here are some upcoming events you might be interested in. Let me know if I've missed any! PragVue 2024 — (September 17, 2024) The first Czech Vue.js conference, taking place in Cinema City - Nový Smíchov Check it out here Vuejs.de Conf — (October 8, 2024 to October 9, 2024) A community-driven Vue conference in Germany. Listen to great talks from great speakers and meet the wonderful VueJS Community. Check it out here Vue Fes Japan 2024 — (October 19, 2024) Check it out here VueConf Toronto 2024 — (November 18, 2024 to November 20, 2024) My favourite Vue conference, in my own backyard! A three-day event with workshops, speakers from around the world, and socializing. Check it out here 💬 Things "Things aren't always #000000 and #FFFFFF." — undefined 🧠 Spaced-repetition: Make Testing Easy 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: - Humble Components — UI is notoriously hard to test, and always has been. So keep as much in Humble Components, components that only receive props and emit events and nothing else. By making our UI as simple as possible we also make it much easier to test.
- Extract logic to composables — And I mean all of your logic. Components (that aren't Humble) should only contain the bare minimum to connect all the different composables together. Think of them as Controller Components, the "C" in MVC.
- Composables are thin layers of reactivity — The easiest thing in the world to test are pure functions that have no dependencies. If you can make the majority of your codebase simple JS or TS code, you've already won. Composables then become simple wrappers that add a layer of reactivity to this business logic.
🔗 Want more Vue and Nuxt links? Michael Hoffman curates a fantastic weekly newsletter with the best Vue and Nuxt links. Sign up for it here.
p.s. I also have four products/courses: Clean Components Toolkit, Vue Tips Collection 2, Mastering Nuxt 3, and Reusable Components |
评论
发表评论