🔥 (#131) Contextually Aware Quiz Components
Hey!
This week I've got some extra content for you, from the Clean Components Toolkit (in addition to your regular weekly dose of tips).
I've been working hard on adding tools to solve how we organize logic in our apps.
It's not enough to know how to split components up. We also need to know how to simplify and organize the logic within those components effectively.
Luckily, the Composition API opens up a whole lot of possibilities for us!
But don't worry, if you're still using the Options API, there are ways to incrementally opt-in that aren't too painful (we covered that last week).
Have an awesome week!
— Michael
🔥 Contextually Aware Quiz Components (Bonus Video)
I recorded a short video to go through how I implemented the quiz functionality in the Clean Components Toolkit.
Here, I use one of my favourite patterns, Contextually Aware Components.
They're sort of magical ✨ and that's why I love them.
🔥 Extract Conditional Pattern (Bonus Tip)
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.
🔥 Prose Components in Nuxt 3
With Nuxt Content 2 we can customize how Markdown gets rendered in our Nuxt 3 apps by creating our own custom Prose
components.
We do get code highlighting built-in through Shiki, but I already have a custom theme for Prism.
So I needed to create a custom ProseCode
component that used PrismJS to render the code blocks from my Markdown:
<template> <pre :class="`language-${language}`" ><code v-html="highlighted"></code></pre> </template>
import Prism from 'prismjs'; const props = withDefaults( defineProps<{ code?: string; language?: string | null; filename?: string | null; highlights?: Array<number>; }>(), { code: '', language: null, filename: null, highlights: [], } ); const highlighted = props.language ? Prism.highlight( props.code, Prism.languages[props.language], props.language ) : props.code;
When we place this component in ~components/content
and name it ProseCode
, Nuxt Content knows to use it when rendering code blocks from Markdown.
We get a few props, and then use PrismJS
to highlight it. This is all done on the server too, so our code is already highlighted before it hits the client.
Note: the formatting inside of the pre
tag looks weird because it will preserve any formatting, including newlines. Moving the code
element to the next line and indenting would cause the code rendered to the page to also have an extra newline and a few spaces in front of it.
You can create custom Prose
components for most elements.
🔥 toRef default value
You've been using toRef
for a while, but did you know you can also supply a default value?
const bank = reactive({ Rand: 3400, Egwene: 20, Matrim: 230340, Padan: -20340, }) // toRef(object, property, default) const myBankAccount = toRef(bank, 'Michael', 1000 * 1000);
Probably the easiest way to become a millionaire.
🔥 Don't set height and width
Setting the height
(or width
) explicitly on an element is a code smell for me, whether you're using px
or percentages or any other units.
Why do I not like it?
Because there's almost always a better way, and the height
property usually makes things more difficult. It also messes with how the flexbox and grid layout systems work.
Instead, it's better to rely on the natural size of the content, padding, borders, and margin.
Of course, there are some caveats where I do like using height
and width
:
- Filling space using
100%
or100vh
- Making specific shapes, like a circle that has a height and width of
50px
(you'll see this all over my site)
I'm more likely to set min-height
or max-height
to create more responsive designs.
📜 Client-Side Error Handling in Nuxt 3
It may not be as fun as shipping the next feature, but making sure our apps are rock-solid and can recover from errors is crucial.
Without good error-handling, our UX suffers and our applications can be hard to use.
In this article I explore handling client-side errors using the NuxtErrorBoundary
component.
Check it out here: Client-Side Error Handling in Nuxt 3
💬 It always takes longer
"It always takes longer than you expect, even when you take into account Hofstadter's Law." — Hofstadter's Law
🧠 Spaced-repetition: Reusability Fundamentals: The Configuration Pattern
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.
So you've got a fantastic CodeBlock
component that does syntax highlighting and even shows line numbers:
<CodeBlock language="js"> const myMessage = 'Highlighting code is supa ez'; </CodeBlock>
But now, you need to support a second colour theme.
Instead of copy and pasting (which is sometimes the right solution!), we can use props to help us create variations:
<!-- Uhhh, maybe not the best solution --> <DarkModeCodeBlock language="js"> const myMessage = 'Highlighting code is supa ez'; </DarkModeCodeBlock>
<!-- This is what props were meant for --> <CodeBlock language="js" theme="darkMode" > const myMessage = 'Highlighting code is supa ez'; </CodeBlock>
You already do this intuitively, so this may not be a huge revelation.
But the Configuration pattern is a fundamental pattern — you can't ignore it if you want to master reusability.
Dealing with prop explosions and understanding the Base Component Pattern is also part of mastering Configuration, the second level of reusability.
And the other, more exciting levels of reusability?
Well, mastering Configuration is vital to unlocking them. All the other levels build on top of this one.
p.s. I also have four products to help you learn Vue: Clean Components Toolkit, Vue Tips Collection, Mastering Nuxt 3 and Reusable Components
评论
发表评论