🔥 (#148) Combining default and named slots, extracting conditionals
Hey there!
I hope you're having a great week.
Here are some more tips for you!
— Michael
🔥 Extract Conditional Pattern
This is one of my favourite tools from the Clean Components Toolkit.
It's super easy to implement, and can be applied in so many situations to clean up your code fairly quickly.
We can apply this in almost any place we have a v-if
in our component.
When we extract each branch body we go from this:
<div v-if="condition"> <div> <!-- Lots of code here --> </div> </div> <div v-else> <div> <!-- Lots of other code --> </div> </div>
To this:
<div v-if="condition"> <NewComponent /> </div> <div v-else> <OtherComponent /> </div>
We know we can do this for two reasons:
- Each branch is semantically related
- Each branch does distinct work
We know that each branch is semantically related, meaning all of that code works together to perform the same task.
Each branch also does distinct work — otherwise, why have a v-if
at all?
This means it's a perfect opportunity to create new components.
And by replacing a large chunk of code with a well-named component that represents the code's intent, we make our code much more self-documenting.
However, this pattern doesn't always work well. If we have a lot of branches we may want to switch to using the Strategy Pattern, another tool included in the Clean Components Toolkit.
🔥 Using default and named slots
Let's say we're building an If
component and we want to use it in two main ways — with a default slot, or with two named slots.
Just the default slot:
<If :val="putConditionHere"> Renders only if it's true </If>
Or using the named slots to access both branches:
<If :val="putConditionHere"> <template #true> Renders only if it's true </template> <template #false> Renders only if the condition is false </template> </If>
This is how we'd have to arrange the slots in order to make this work:
<template> <slot v-if="val" /> <template v-if="!$slots.default"> <slot v-if="val" name="true" /> <slot v-if="!val" name="false" /> </template> </template>
You'll notice we use a template
to group the named slots. We're also checking $slots
to see if we've put anything in the default slot or not.
We need to do this, otherwise we'll render the default
and true
slots whenever our condition is true, which isn't what we want! We want these to be mutually exclusive — you can either use the default slot or the named true
slot.
Now, we can use it in either way.
Just the default slot to access the true branch, or using two named slots to access both the true and false branches.
🔥 Make Testing Easy
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.
- Thin Composables — 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.
These tools from Clean Components Toolkit will help you do that.
📜 Compressing Images with Vite and VSharp
Images are one of the biggest causes of slow webpages.
We have a bunch of strategies for dealing with this, but the most basic one is to make sure each and every image in your app is compressed as much as possible.
Fortunately for us, setting up Nuxt to automatically compress our images only takes a few minutes.
Check it out here: Compressing Images with Vite and VSharp
💬 The Goal
"The goal of software development is to build something that lasts at least until we've finished building it." — Anonymous
🧠 Spaced-repetition: 6 Levels of Reusability
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.
There are six different levels of reusability that you can use in your components.
Each level increases your ability to reuse code.
These levels are the focus of my course, Reusable Components.
Here are the six levels of reusability:
- Templating — Reusing code by wrapping it up inside of a component
- Configuration — Using configuration props to allow for varying behaviour
- Adaptability — Allowing components to become future-proof
- Inversion — Letting other components control the process
- Extension — Using reusability throughout our component
- Nesting — Creating powerful hierarchies of components
I cover this in more detail in this excerpt from the course.
p.s. I also have four products/courses: Clean Components Toolkit, Vue Tips Collection 2, Mastering Nuxt 3, and Reusable Components
评论
发表评论