🔥 (#205) Flatten Nuxt Content Routes, Mock Any Import, and more

Read this on my blog

Hey!

I hope you're having a wonderful week.

Where I'm living in Waterloo, we've already had 140 cm of snow this February. That's almost as much snow in the last 3 weeks as we usually get in an entire winter.

I'm so tired from shoveling snow 🥲

This week I've been working on the lesson outlines for Mastering Nuxt while we get a new design done for the app that we build in the course.

And as always, I've got some tips and articles for you!

— Michael

🔥 Flatten Nuxt Content Routes

I wanted to organize my blog content into several folders:

  • Articles: content/articles/
  • Newsletters: content/newsletters/

By default though, Nuxt Content would set up these routes to include those prefixes. But I want all of my routes to be at the root level:

  • Articles: michaelnthiessen.com/my-latest-article
  • Newsletters: michaelnthiessen.com/most-recent-newsletter

We can do this manually for each Markdown file by overriding the _path property through it's frontmatter:

---  title: My Latest Article  date: today  _path: "/my-latest-article"  ---

This is extremely tedious, error-prone, and generally annoying.

Luckily, we can write a simple Nitro plugin that will do this transform automatically.

Create a content.ts file in server/plugins/:

export default defineNitroPlugin((nitroApp) => {    nitroApp.hooks.hook('content:file:afterParse', (file) => {      for (const prefix of ['/articles', '/newsletters']) {        if (file._path.startsWith(prefix)) {          // Keep the prefix so we can query based on it still          file._original_dir = prefix;          // Remove prefix from path          file._path = file._path.replace(prefix, '');        }      }    });  });

Nitro is the server that Nuxt uses internally. We can hook into it's processing pipeline and do a bit of tweaking.

However, doing this breaks queryContent calls if we're filtering based on the path, since queryContent is looking at the _path property we've just modified. This is why we want to keep that original directory around.

We can modify our queryContent calls to filter on this new originaldir property:

// Before  queryContent('/articles')    // After  queryContent()    .where({      _original_dir: { $eq: '/articles' },    });

Pro tip: use nuxi clean to force Nuxt Content to re-fetch and re-transform all of your content.

🔥 Directly accessing parent components (and why)

Props down, events up. That's how your components should communicate — most of the time.

But in rare cases, that just doesn't work.

If you need direct access to the parent component, you should just use provide/inject to pass down the relevant value or method:

import { provide } from 'vue';  const someMethodInTheParent = () => {};  provide('method', someMethodInTheParent)

Then, inject it into the child component:

import { inject } from 'vue';  const method = inject('method');  method();

In Vue 2, you can also use the instance property $parent:

// Tight coupling like this is usually a bad idea  this.$parent.methodOnParentComponent();

This is simpler, but leads to higher coupling and will more easily break your application if you ever refactor.

You can also get direct access to the application root, the very top-most component in the tree, by using $root. Vue 2 also has $children, but these were taken out for Vue 3 (please don't use this one).

When would these be useful?

There are a few different scenarios I can think of. Usually, when you want to abstract some behaviour and have it work "magically" behind the scenes.

You don't want to use props and events to connect up a component in those cases. Instead, you use provide/inject, $parent, or $root, to automatically connect the components and make things happen.

(This is similar to the Compound Component pattern)

But it's hard to come up with an example where this is the best solution. Using provide/inject is almost always the better choice.

🔥 Mock Any Import in Nuxt

One handy helper method in @nuxt/test-utils is mockNuxtImport.

It's a convenience method to make it easier to mock anything that Nuxt would normally auto-import:

import { mockNuxtImport } from '@nuxt/test-utils/runtime';    mockNuxtImport('useAsyncData', () => {    return () => {      return { data: 'Mocked data' };    };  });    // ...tests

🎙️ #047 — A Vue at Alexander Lichter

If you've listened to the last episode, you know what is coming next!

It is time to get take a Vue at the other host of this podcast. Michael is asking Alex all around his past - from how we got into programming and web development, if university was worth it and how he got into the Nuxt Core Team.

Also don't miss out how Minecraft is part of the history, what non-tech job Alex would do if programming wouldn't be in the cards, and why is GitHub account is over 14 years old.

Watch on YouTube or listen on your favorite podcast platform.

Chapters:

In case you missed them:

📜 Exploring Server Components in Nuxt

You may have heard about server components, but there are some really interesting things you can do with them.

In this article I explore a few really cool things we can do with server components.

Check it out here: Exploring Server Components in Nuxt

📅 Upcoming Events

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

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

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

Giving a talk here on component patterns! A great Vue conference, this year held in Tampa. Two days of conference talks, plus a day for workshops.

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

💬 The real problem

"Sometimes the problem is to discover what the problem is." — Gordon Glegg

🧠 Spaced-repetition: Default Content with Slots

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 provide fallback content for a slot, in case no content is provided:

<!-- Child.vue -->  <template>    <div>      <slot>        Hey! You forgot to put something in the slot!      </slot>    </div>  </template>

This content can be anything, even a whole complex component that provides default behaviour:

<!-- Child.vue -->  <template>    <div>      <slot name="search">        <!-- Can be overridden with more advanced functionality -->        <BasicSearchFunctionality />      </slot>    </div>  </template>

🔗 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

评论

此博客中的热门博文

🔥 (#155) A Vue podcast?

Scripting News: Monday, November 20, 2023