🔥 (#126) The Configuration Pattern
Hey!
This week I'm at the cottage to take full advantage of summer.
I've scheduled this one ahead of time, since I'm hanging out with family, reading a lot, and don't have any internet access right now.
(So yes, I'm writing this in the past as though I'm writing it to you in the future...?)
I hope you're enjoying your summer (or winter) as well!
Here are some tips, as always.
Have a fantastic week!
— Michael
Vue Tips Collection
Maybe you just want to stay on top of the latest features, remind yourself of interesting things Vue can do, and get daily inspiration.
Vue Tips Collection is a beautiful book of 115 awesome tips, as well as a daily email to get your creative juices flowing.
🔥 Reusability Fundamentals: The Configuration Pattern
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.
🔥 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; } }
🔥 Suspense: More Flexible Loading State
You've probably written lots of components that handle their own loading state.
Either while data is being fetched or while an async component is loaded.
But with the new Suspense
component, we can instead handle that loading state further up the component tree:
<template> <Suspense> <!-- Shown once ChatWindow loads --> <template #default> <ChatWindow /> </template> <!-- Loading state --> <template #fallback> <Spinner color="blue" /> </template> </Suspense> </template>
<script setup> import { defineAsyncComponent } from "vue"; import Spinner from "@/components/Spinner.vue"; // ChatWindow will load asynchronously, but it doesn't // have to have any loading state at all. Or even know // that it's being loaded asynchronously! const ChatWindow = defineAsyncComponent( () => import("@/components/ChatWindow") ); </script>
This also works if the child component returns a Promise from the setup
function:
// async functions always return a Promise async setup() { const { users } = await loadData(); return { users, }; }
Even better is that the async child component can be anywhere as a descendant.
This means you can have a "root" loading state in your app, and any async components anywhere will trigger this loading state:
<!-- App.vue --> <template> <Suspense> <template #default> <AppWithAsyncBehaviour /> </template> <template #fallback> <FullPageLoading /> </template> </Suspense> </template>
📜 Auth in Nuxt 3: Setting Up Supabase Auth with Nuxt 3 (1 of 4)
Authentication can be tricky to implement, but it lets us do so much in our apps.
In this first article of this series, we'll go over how to set up and configure Supabase in our Nuxt project.
Here are the main steps we'll cover in this article:
- Create the Supabase project
- Set up the Github OAuth app
- Install the Nuxt 3 Supabase module
- Adding in our environment variables
Check it out here: Auth in Nuxt 3: Setting Up Supabase Auth with Nuxt 3 (1 of 4)
💬 First Do It
"First do it, then do it right, then do it better." — Addy Osmani
🧠 Spaced-repetition: Inline Composables
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.
You can define composables inline, keeping them in your SFC file:
<script setup> const useCount = (i) => { const count = ref(0); const increment = () => count.value += 1; const decrement = () => count.value -= 1; return { id: i, count, increment, decrement, }; }; const listOfCounters = []; for (const i = 0; i < 10; i++) { listOfCounters.push(useCount(i)); } </script> <template> <div v-for="counter in listOfCounters" :key="counter.id"> <button @click="counter.decrement()">-</button> {{ counter.count }} <button @click="counter.increment()">+</button> </div> </template>
But is there any point to doing this?
If you're keeping your components focused on a specific task (and you should be), then it stands to reason that the logic is also focused on a single task.
This means that if you wrap up all relevant logic into an inline composable, you've wrapped up all — or nearly all — the logic that this component has:
<script setup> // Create an inline composable const useStuff = () => { <all_our_logic> }; // ...only to destructure most of it to use in our template const { value, anotherValue, eventHandler, anotherEventHandler } = useStuff(); </script>
At which point, we might as well write our logic without that unnecessary wrapper:
<script setup> const value = ... const anotherValue = ... const eventHandler = ... const anotherEventHandler = ... </script>
However, if you have do have logic that can be encapsulated nicely within this inline composable, it could make your code cleaner and easier to use.
Using lexical scoping to create more boundaries within your files helps you to understand and think through your code, which is always helpful.
p.s. I also have four courses: Vue Tips Collection, Mastering Nuxt 3, Reusable Components and Clean Components
评论
发表评论