My latest project is a baby girl!
We welcomed baby Thiessen in to our family a couple weeks ago, so I've been off work and enjoying the sleepless nights and getting to know my daughter.
There have been some challenges, but I'm really enjoying fatherhood! It's truly incredible how a baby that really doesn't do much can be endlessly fascinating.
Of course, I want to celebrate — but I have limited time (and mental capacity) for articles and videos.
So I'm putting everything at 30% off until Saturday, April 27!
Just use the code BABYTHIESSEN at the checkout.
This will apply to all of these:
I'm also opening up orders again for the Vue Tips Collection hardcover book. But I have limited time to package and ship things, so I'm only selling 30 copies (unlimited digital copies though).
And if you just wanted the tips, well, here they are.
— Michael
🔥 Dynamic Returns
A composable can either return a single value or an object of values. Typically, these values are refs
.
But we can also dynamically switch between the two depending on what we want to use the composable for:
// Grab only the single value
const now = useNow()
// Get more granular access to the composable
const {
now,
pause,
resume
} = useNow({ controls: true })
This is great because we may only need a single value most of the time. So why complicate the interface for the main use case?
But by dynamically providing the full object of ref
s, we allow for more advanced use cases as well. Even if they are rarely used.
Here is how we might implement that:
export default useNow(opts) {
const {
controls = false,
} = opts;
// Do some things in your composable
if (controls) {
return { now, pause, resume };
} else {
return now;
}
}
🔥 Reusing Code and Knowledge
Don't Repeat Yourself — an acronym that many know but many don't correctly understand.
DRY isn't actually about code, it's about the knowledge and decisions that are contained in the code. Too often we are just pattern matching on syntax, and that leads us to bad abstractions that should never exist.
Here are some ways we can fix that:
- Don't Repeat Yourself (DRY) — Use components and composables to create reusable views and logic. Doing this well is an entire topic all on it's own, which is why I created a whole course on it.
- Optimize for Change — Most of our time is spent modifying existing code, so it pays to make it easy. If code is new or likely to change, don't worry about abstracting it into a new component yet — duplication is welcome here.
- Syntax vs. Knowledge — When removing duplication or "drying up" your code, make sure you're encapsulating knowledge and not syntax. Just because code looks the same doesn't mean it is the same.
🔥 Example of a Composable Using the Options Object Pattern
Let's create a useEvent
composable that will make it easier to add event listeners.
We'll use the EventTarget.addEventListener
method, which require the event
and handler
parameters. These are the first two required parameters:
export function useEvent(event, handler) {};
But we also need to know which element to target. Since we can default to the window
, we'll make this our first option:
export function useEvent(event, handler, options) {
// Default to targeting the window
const { target = window } = options;
};
Then we'll add in onMounted
and onBeforeUnmount
hooks to setup and clean up our event:
import { onMounted, onBeforeUnmount } from 'vue';
export function useEvent(event, handler, options) {
// Default to targeting the window
const { target = window } = options;
onMounted(() => {
target.addEventListener(event, handler);
});
onBeforeUnmount(() => {
target.removeEventListener(event, handler);
});
};
We can use the composable like this:
import useEvent from '~/composables/useEvent.js';
// Triggers anytime you click in the window
useEvent('click', () => console.log('You clicked the window!'));
The addEventListener
method can also take extra options, so let's add support for that, too:
import { onMounted, onBeforeUnmount } from 'vue';
export function useEvent(event, handler, options) {
// Default to targeting the window
const {
target = window,
...listenerOptions
} = options;
onMounted(() => {
target.addEventListener(event, handler, listenerOptions);
});
onBeforeUnmount(() => {
target.removeEventListener(event, handler, listenerOptions);
});
};
We keep listenerOptions
as a pass-through, so we're not coupling our composable with the addEventListener
method. Beyond hooking up the event, we don't really care how it works, so there's no point in interfering here.
Now we can take advantage of those extra options:
import useEvent from '~/composables/useEvent.js';
// Triggers only the first time you click in the window
useEvent(
'click',
() => console.log('First time clicking the window!'),
{
once: true,
}
);
This is a pretty basic composable, but by using the Options Object Pattern it's easily configurable and extendable to cover a wide swath of use cases.
📜 Coding Better Composables: Flexible Arguments (2/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 second article is on using ref
and unref
to make the arguments more flexible.
Check it out here: Coding Better Composables: Flexible Arguments (2/5)
💬 Writing Love Letters
"Documentation is a love letter that you write to your future self." — Damian Conway
🧠 Spaced-repetition: Dedupe fetches in Nuxt
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.
Since 3.9 we can control how Nuxt deduplicates fetches with the dedupe
parameter:
useFetch('/api/menuItems', {
// Cancel the previous request and make a new request
dedupe: 'cancel'
});
The useFetch
composable (and useAsyncData
composable) will re-fetch data reactively as their parameters are updated. By default, they'll cancel the previous request and initiate a new one with the new parameters.
However, you can change this behaviour to instead defer
to the existing request — while there is a pending request, no new requests will be made:
useFetch('/api/menuItems', {
// Keep the pending request and don't initiate a new one
dedupe: 'defer'
});
This gives us greater control over how our data is loaded and requests are made.
p.s. I also have four products/courses: Clean Components Toolkit, Vue Tips Collection 2, Mastering Nuxt 3, and Reusable Components
评论
发表评论