🔥 (#183) Default and Named Slots, Global Components, and the limitations of props

Hey there,

If you missed it, last week I released a free mini-course on building Vue 3 based Reactivity from Scratch, and already over 1000 people signed up!

But I have some more exciting news this week.

If you write tests, I've got an advanced workshop series coming up!

I've spent the last 6+ weeks doing customer interviews, in-depth research, and putting together this workshop. In it, you'll learn how to:

  • Revolutionize your testing strategy — Discover how to craft a tailored testing shape that goes beyond the testing pyramid and trophy and truly fits your project's needs
  • Elevate your mocking skills — Gain mastery over advanced mocking techniques and learn to identify when excessive mocking signals underlying code issues
  • Maximize your testing ROI — Learn to strategically prioritize your tests across unit, component, and end-to-end levels for optimal coverage and efficiency
  • Write tests that stand the test of time — Master best practices for creating crystal-clear, maintainable tests and refactoring your code for enhanced testability

and more!

It's actually 3 workshops bundled together, so you get really good value for your money.

Find more details here, and please email me if you have any questions about it!

Have a great week!

— Michael

🔥 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.

🔥 Global Components

When you register a component globally, you can use it in any template without importing it a second time:

// Vue 3  import { createApp } from 'vue';  import GlobalComponent from './GlobalComponent.vue';  const app = createApp({})  app.component('GlobalComponent', GlobalComponent);

In Vue 2 you can register global components like this:

// Vue 2  import Vue from 'vue';  import GlobalComponent from './GlobalComponent.vue';  Vue.component('GlobalComponent', GlobalComponent);

Now you can use GlobalComponent in your templates without any extra work!

Of course, globally registered components have the same pros and cons as global variables. So use this sparingly.

🔥 The limitations of props

Props are helpful, but they have two glaring issues:

  • Impossible to pass markup*
  • Not that flexible

*not technically impossible, but not something you want to do.

The solution to these two problems is the same, but we'll get there in a second.

Many components you create are contentless components. They provide a container, and you have to supply the content. Think of a button, a menu, an accordion, or a card component:

<Card title="Shrimp Tempura">    <img src="picOfShrimp.jpg" />    <div>      <p>Here are some words about tempura.</p>      <p>How can you go wrong with fried food?</p>    </div>    <a href="www.michaelnthiessen.com/shrimp-tempura">      Read more about Shrimp Tempura    </a>  </Card>

You can often pass this content in as a regular String. But many times, you want to pass in a whole chunk of HTML, maybe even a component or two.

You can't do that with props.*

*again, yes, you could do this, but you'll definitely regret it.

Props also require that you plan for all future use cases of the component. If your Button component only allows two values for type, you can't just use a third without modifying the Button:

<!-- You just have to *believe* it will work -->  <Button type="AWESOME" />

The component doesn't allow for that...

<!-- Button.vue -->  <script setup>  defineProps<{    type: 'Primary' | 'Secondary' // AWESOME is not an option here  }>();  </script>

I'm not a psychic, and I'm guessing you aren't either.

The solution to these problems?

I think I gave it away with my card example above...

...slots!

Slots allow you to pass in whatever markup and components you want, and they also are relatively open-ended, giving you lots of flexibility. This is why in many cases, slots are simply better than props.

🎙️ #025 — Nuxt Modules and Open Source at work (with Lucie Haberer)

In this episode, Alex is joined by fellow Nuxt core team member Lucie Haberer, who is not only a DX Engineer at Prismic, doing open source but also public speaking.

Together they talk about Lucie's recent free Nuxt and Nuxt UI course and how she got into Vue and Nuxt. Lucie explains further why she fell in love with Nuxt modules and which modules she built so far - with some enlightening surprise there!

In addition, they talk about how Open Source works at Prismic - from when they do open source over to sponsoring projects and contributing - and many more insights. 

Enjoy the episode!

Watch on YouTube or listen on your favorite podcast platform.

Chapters:

In case you missed them:

📜 Configuration in Nuxt 3: runtimeConfig vs. appConfig

Nuxt 3 provides powerful configuration options, allowing you to adapt your application to different use cases.

The two key parts of Nuxt 3's configuration system are runtimeConfig and appConfig.

This article will explain the purpose and differences between these two options and show you how to use them.

Check it out here: Configuration in Nuxt 3: runtimeConfig vs. appConfig

📜 The 12 Best Features in Nuxt 3

This is a high-level overview of 12 of the most crucial features in Nuxt 3.

I explain why each is important, and how Nuxt 3 works hard to make your dev experience as effortless as possible.

Check it out here: The 12 Best Features in Nuxt 3

📅 Upcoming Events

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

Vuejs.de Conf — (October 8, 2024 to October 9, 2024)

A community-driven Vue conference in Germany. Listen to great talks from great speakers and meet the wonderful VueJS Community.

Check it out here

Vue Fes Japan 2024 — (October 19, 2024)

Check it out here

VueConf Toronto 2024 — (November 18, 2024 to November 20, 2024)

My favourite Vue conference, in my own backyard! A three-day event with workshops, speakers from around the world, and socializing.

Check it out here

Vuejs Amsterdam 2025 — (March 12, 2025 to March 13, 2025)

The biggest Vue conference in the world! A two-day event with workshops, speakers from around the world, and socializing.

Check it out here

💬 Libraries

"Telling a programmer there's already a library to do X is like telling a songwriter there's already a song about love." — Pete Cordell

🧠 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 four products/courses: Clean Components Toolkit, Vue Tips Collection 2, Mastering Nuxt 3, and Reusable Components

Unsubscribe

评论

此博客中的热门博文

Scripting News: Tuesday, June 11, 2024

Scripting News: Tuesday, February 13, 2024