Hey! This week I've been thinking a lot about how Vue and Nuxt continue to evolve. The ecosystem keeps getting better, and it's exciting to see where things are headed. Here are your weekly tips, articles, and other Vue and Nuxt goodies. — Michael Clean Components Toolkit Are your Vue components getting messy and hard to maintain? Struggling with when to split components or how to organize your code? The Clean Components Toolkit teaches you battle-tested patterns and principles to write cleaner, more maintainable Vue apps, including: - 3.5 + hours of focused video content plus comprehensive written materials
- Step-by-step refactoring examples showing real-world applications
- Interactive quizzes to reinforce your learning
- 20 + practical tools and patterns for component organization
- Lifetime access to updates and new content
You'll master: - Component splitting and combining — when (and when not) to break up components
- State management across components — especially as complexity grows
- Logic organization and reuse — using the three core component types
- Seamless refactoring techniques — transform messy code into clean, maintainable components
"The Clean Components Toolkit's concise, to-the-point lessons made the learning process feel effortless and led to a deeper understanding of the subject matter." — Alex Rodriguez Master Clean Components Now → 🔥 Using useRequestHeader Grabbing a single header from the request couldn't be easier in Nuxt: const contentType = useRequestHeader('content-type');
This is super handy in middleware and server routes for checking authentication or any number of things. If you're in the browser though, it will return undefined. This is an abstraction of useRequestHeaders, since there are a lot of times where you need just one header. See the docs for more info. 🔥 Inline Composables 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> <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. 🔥 Component Variations with the Base Component Pattern The Base Component pattern is one of my favourite ways to make many different versions and variants from a single component. It has a few basic steps: - Create your base component
- Wrap it with another component to get a variant of the original
- Repeat step 2 as many times as you need
Here's an example, creating a DisabledButton variant out of a BaseButton component: <!-- DisabledButton.vue --> <template> <!-- Never forget how to create this disabled button. Package it up using the Base Component pattern. --> <BaseButton type="disabled" disabled > <!-- You can't accidentally use the wrong icon now. It's provided here for you --> <template #icon> <Icon type="disabled" /> </template> </BaseButton> </template>
You can use this pattern in many different ways: - Lock down props — take a
Button component and hard code a few props to get a DisabledButton. Now you can just use the DisabledButton directly without fiddling with all the necessary props each time. - Lock down slots — create an
InfoButton variant where the icon passed to the Button is always the same. So now, if you ever need to change the icon (or anything else), you can do it in one place. - Simplify props — sometimes components end up with dozens of props, primarily for edge cases. Instead, create a
BaseButton with all the props, and a Button that passes on only the most common ones. This is a lot safer and easier to use, and the documentation for this component is easier to read. I've included more on this pattern in Reusable Components. 📜 21 Nuxt Tips You Need to Know In this article I share 21 tips for working with Nuxt. These are tips and tricks that I've found useful, and I think every Nuxt developer should know. We cover a lot of topics here, including: - When to use /assets vs. /public directory
- Using runtimeConfig vs. app.config
- Understanding how Universal rendering works (and how it's different from SPA and SSR)
Check it out here: 21 Nuxt Tips You Need to Know 📜 12 Design Patterns in Vue Design patterns are incredibly useful in writing code that works well over the long run. They let us use proven solutions to problems that basically every single app has. But there isn't a lot written about how design patterns apply specifically to Vue. In this article, I cover 12 design patterns that I think are crucial to writing great Vue apps. Check it out here: 12 Design Patterns in Vue 💬 Solve the Problem "First, solve the problem. Then, write the code." — John Johnson 🧠 Spaced-repetition: Avoid Ref Soup 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. With reactive objects we can organize our state into objects instead of having a bunch of refs floating around: // Just a bunch a refs :/ const firstName = ref('Michael'); const lastName = ref('Thiessen'); const website = ref('michaelnthiessen.com'); const twitter = ref('@MichaelThiessen');
Passing around a single object instead of lots of refs is much easier, and helps to keep our code organized: // A single object to think about const michael = reactive({ firstName: 'Michael', lastName: 'Thiessen', website: 'michaelnthiessen.com', twitter: '@MichaelThiessen', });
There's also the added benefit that it's much more readable. When someone new comes to read this code, they know immediately that all of the values inside of a single reactive object must be related somehow — otherwise, why would they be together? With a bunch a refs it's much less clear as to how things are related and how they might work together (or not). However, an even better solution for grouping related pieces of reactive state might be to create a simple composable instead: // Similar to defining a reactive object const michael = usePerson({ firstName: 'Michael', lastName: 'Thiessen', website: 'michaelnthiessen.com', twitter: '@MichaelThiessen', }); // We usually return refs from composables, so we can destructure here const { twitter } = michael;
This gives us the benefits of both worlds. And if we need to add a method that modifies this state it can go in the composable. You can't do that with ref soup or a reactive object*. *you can, but you should first stop and ask yourself if you should. 🔗 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 a bunch of products/courses: |
评论
发表评论