🔥 (#213) Recursive slots, lonely children, and extracting conditional patterns

Read this on my blog

Hey!

This week you can get the Vue Tips Collection hardcover for only $47.

That includes free shipping to the US, Canada, and the EU. It's 220 pages, full color, with 118 tips across 7 themed chapters. Syntax, reactivity, patterns, everything you could want.

Go here to grab your copy.

Of course, I've always got tips for you (and more!) in this email.

Have a great week!

— Michael

🔥 Recursive slots

I decided to see if I could make a v-for component using only the template. Along the way, I discovered how to use slots recursively, too.

This is what the component looks like:

<!-- VFor.vue -->  <template>    <div>      <!-- Render the first item -->            <!-- If we have more items, continue!           But leave off the item we just rendered -->      <v-for        v-if="list.length > 1"        :list="list.slice(1)"      />    </div>  </template>

If you wanted to do this with scoped slots — and why wouldn't you?! — it just takes a few tweaks:

<template>    <div>      <!-- Pass the item into the slot to be rendered -->      <slot v-bind:item="list[0]">        <!-- Default -->              </slot>        <v-for        v-if="list.length > 1"        :list="list.slice(1)"      >        <!-- Recursively pass down scoped slot -->        <template v-slot="{ item }">          <slot v-bind:item="item" />        </template>      </v-for>    </div>  </template>

Here is how this component is used:

<template>    <div>      <!-- Regular list -->      <v-for :list="list" />        <!-- List with bolded items -->      <v-for :list="list">        <template v-slot="{ item }">          <strong></strong>        </template>      </v-for>    </div>  </template>

For a more detailed explanation of this example and nested slots, check out my blog post on it:

How to Use Nested Slots in Vue (including scoped slots)

🔥 Lonely children

Here's a technique that makes it super easy to simplify nested elements:

You take everything inside a v-if or v-for and extract it into a new component.

You'll go from this, with some nesting:

<template>    <div>      <!-- ... -->      <div v-for="item in list">        <h2 class="item-title">                  </h2>        <p class="item-description">                  </p>      </div>      <!-- ... -->    </div>  </template>

To this, where the nesting is gone:

<template>    <div>      <!-- ... -->      <ListItem        v-for="item in list"        :item="item"      />      <!-- ... -->    </div>  </template>

To do this, you extract the code in the v-for into a new component:

<!-- ListItem.vue -->  <template>    <div>      <h2 class="item-title">              </h2>      <p class="item-description">              </p>    </div>  </template>

This technique becomes more and more valuable the more nesting you have.

Note: You can choose to do this recursively, taking every v-for or v-if and creating a new component. But often, it's simpler to grab a more significant chunk of the template and remove most of the nesting with one new component.

I share more about this in my course, Clean Component Toolkit.

🔥 Extract Conditional Pattern

An extremely common question I get asked all the time is, "how do you know when to split up a component?"

I want to share a simple pattern with you that is basically fool-proof, and can be applied to lots of components with almost no thought.

When we encounter a v-if (or v-show) in our template, there's one main pattern we can use:

Extracting the body of each branch into its own component.

This is just one of many patterns included in Clean Components. There, we go into much more detail, examining each pattern more closely and really fine-tuning our understanding.

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. But we'll get to that later on.

🎙️ #055 — e18e: Ecosystem Performance (with James Garbutt)

In this episode of DejaVue, Alex sits down with James Garbutt, open source maintainer and lead of the e18e initiative. James shares his journey from writing web scrapers as a teenager to maintaining critical JavaScript libraries like parse5 or Chokidar and eventually co-creating the ecosystem performance initiative.

The conversation is then all around e18e, which aims to improve performance across the JavaScript ecosystem through three pillars:

  • Cleaning up dependency trees
  • Speeding up popular packages
  • Creating lighter alternatives to bloated libraries

James explains how the community-driven approach has produced impressive results all across the web development landscape.

Learn about real-world examples of performance improvements, including replacement packages like tinyglobby and nano-staged, and discover how to contribute to e18e even if you're new to open source. James shares also insights on balancing between backward compatibility and performance, bundling dependencies, and also shares future plans for e18e in 2025.

Watch on YouTube or listen on your favorite podcast platform.

Chapters:

In case you missed them:

📜 Handling Assets in Nuxt

Nuxt gives us two great options for managing all of our assets in our web app.

But what's the difference, and how do you know which to use?

In this article I'll share some simple questions you can ask yourself to figure out where to put your assets, as well as the differences between the two strategies.

Check it out here: Handling Assets in Nuxt

📜 Prisma with Nuxt: Creating the Prisma Schema (2 of 5)

Trying to manage database schemas alongside your Nuxt app types can be a challenge.

But with Prisma, most of these problems go away.

It handles all of the boilerplate and coordination, so you just write one single schema that's used in your database and in your TypeScript app.

In this article I show you how to create your Prisma schema.

Check it out here: Prisma with Nuxt: Creating the Prisma Schema (2 of 5)

📅 Upcoming Events

Here are some upcoming events you might be interested in. Let me know if I've missed any!

VueConf US 2025 — (May 13, 2025 to May 15, 2025)

VueConf US 2025 is a great Vue conference, this year held in Tampa from May 13–15, with two days of talks and a day of workshops. Unfortunately, I am no longer able to make it to the conference this year. I hope everyone attending has an amazing time and I look forward to joining in the future!

Check it out here

MadVue 2025 — (May 29, 2025)

It's time to get together in Madrid. Join for a full day of talks, activities, and networking with the Vue.js community and ecosystem.

Check it out here

💬 "Users"

"There are only two industries that refer to their customers as 'users'." — Edward Tufte

🧠 Spaced-repetition: Creating Magic with Context-Aware Components

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.

Context-aware components are "magical" — they adapt to what's going on around them automatically, handling edge cases, state sharing, and more.

There are 3 main types of context-aware components, but configuration is the one I find most interesting.

1. State Sharing

When you break up a large component into smaller ones, they often still need to share state.

Instead of pushing that work on whoever's consuming the components, you can make this happen "behind the scenes."

To give you more flexibility, you may break up a Dropdown component into Select and Option components. But to make it easier to use, the Select and Option components share the selected state with each other:

<!-- Used as a single component for simplicity -->  <Dropdown v-model="selected" :options="[]" />    <!-- Split up for more flexibility -->  <Select v-model="selected">    <Option value="mustard">Mustard</Option>    <Option value="ketchup">Ketchup</Option>    <div class="relish-wrapper">      <Option value="relish">Relish</Option>    </div>  </Select>

2. Configuration

Sometimes component behaviour needs to change based on what's going on in the rest of the application. This is often done to automagically handle edge cases that would otherwise be annoying to deal with.

A Popup or Tooltip should reposition itself so it doesn't overflow out of the page. But if that component is inside a modal, it should move, so it doesn't overflow out of the modal.

This can be done automagically if the Tooltip knows when it's inside a modal.

3. Styling

You already create context-aware CSS, applying different styles based on what's happening in parent or sibling elements.

.statistic {    color: black;    font-size: 24px;    font-weight: bold;  }    /* Give some separation between stats     that are right beside each other */  .statistic + .statistic {    margin-left: 10px;  }

CSS variables let us push this further, allowing us to set different values in different parts of the page.

🔗 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:

Unsubscribe

评论

此博客中的热门博文

Weekly Vue News #141 - Share Styling Using Wrapper Components

Scripting News: Tuesday, October 8, 2024

Scripting News: Tuesday, August 20, 2024