🔥 (#191) Nested ref properties, mock API routes, and more

Read this on my blog

Hey all!

Yesterday I gave my talk on Nuxt server components at Nuxt Nation (which is currently still happening if you want to check it out).

Next week I'll be giving a talk on Nuxt layers at VueConf Toronto.

Other than that, I hope you're having a great week!

Enjoy the tips, new podcast episode, and articles.

— Michael

🔥 Smooth dragging (and other mouse movements)

If you ever need to implement dragging or to move something along with the mouse, here's how you do it:

  • Always throttle your mouse events using requestAnimationFrame. Lodash's throttle method with no wait parameter will do this. If you don't throttle, your event will fire faster than the screen can even refresh, and you'll waste CPU cycles and the smoothness of the movement.
  • Don't use absolute values of the mouse position. Instead, you should check how far the mouse has moved between frames. This is a more reliable and smoother method. If you use absolute values, the element's top-left corner will jump to where the mouse is when you first start dragging. Not a great UX if you grab the element from the middle.

Here's a basic example of tracking mouse movements using the Composition API. I didn't include throttling in order to keep things clearer:

// In your setup() function  window.addEventListener("mousemove", (e) => {    // Only move the element when we're holding down the mouse    if (dragging.value) {      // Calculate how far the mouse moved since the last      // time we checked      const diffX = e.clientX - mouseX.value;      const diffY = e.clientY - mouseY.value;      // Move the element exactly how far the mouse moved      x.value += diffX;      y.value += diffY;    }    // Always keep track of where the mouse is    mouseX.value = e.clientX;    mouseY.value = e.clientY;  });

Here's the full example. You can check out a working demo here:

<template>    <div class="drag-container">      <img        alt="Vue logo"        src="./assets/logo.png"        :style="{          left: `${x}px`,          top: `${y}px`,          cursor: dragging ? 'grabbing' : 'grab',        }"        draggable="false"        @mousedown="dragging = true"      />    </div>  </template>
<script setup>  import { ref } from "vue";  const dragging = ref(false);  const mouseX = ref(0);  const mouseY = ref(0);  const x = ref(100);  const y = ref(100);  window.addEventListener("mousemove", (e) => {    if (dragging.value) {      const diffX = e.clientX - mouseX.value;      const diffY = e.clientY - mouseY.value;      x.value += diffX;      y.value += diffY;    }    mouseX.value = e.clientX;    mouseY.value = e.clientY;  });  window.addEventListener("mouseup", () => {    dragging.value = false;  });  </script>

🔥 Nested Ref Properties in Templates

One thing that's a little tedious with refs is when you need to access a nested property within the template:

<template>    <div id="app">      <p v-for="el in arr"></p>    </div>  </template>
const arr = reactive([]);  arr.push(ref({ text: 'hello' }));  arr.push(ref({ text: 'world' }));  setTimeout(() => (arr[0].value.text = 'nothing'), 1000);

You can't just rely on auto-unwrapping of refs, you have to explicitly access the .value and then grab the nested property from there:

ref.value.nestedProperty

In this case, using a reactive value might be preferable — if the syntax is really bothering you.

🔥 Easily Mock API Routes in Nuxt

If you've ever written unit tests, you'll have needed to mock out API endpoints that are used in your components or stores.

With @nuxt/test-utils this is really simple, because you get the registerEndpoint utility method:

import { registerEndpoint } from '@nuxt/test-utils/runtime';  import userTestData from './userTestData.json';  registerEndpoint('/users/', () => userTestData);  // ...tests

You can mock any server route (API endpoint), including external endpoints if you need.

🎙️ #033 — Vue or React? (with CJ from Syntax)

Alex is accompanied by the wonderful CJ Reynolds in this episode of DejaVue. The Senior Creator at Syntax.fm brings not only Vue experience but also a history of Angular JS and React, as well as various other technologies.

The two content creators talk about how CJ became a senior creator at the well-known Syntax.fm podcast and how it is different from streaming on Twitch and his previous content creation processes. Further, CJ gives insights on how the Denver Vue meetup evolved (now the DenverScript meetup) and shares some hopes when it comes to the meetup scene.

Alex and CJ then discuss more technical topics - for example why CJ never fully switched over to Vue but still writes it a lot.

The discussion eventually goes into comparisons between Vue and React, highlighting what Vue does "better" than React and how the Vue ecosystem shapes the web development work.

Watch on YouTube or listen on your favorite podcast platform.

Chapters:

In case you missed them:

📜 Controlling When Components Are Loaded in Nuxt

Nuxt gives us a few different options for controlling when components are loaded.

In this article I explore the different options and how to use each.

Check it out here: Controlling When Components Are Loaded in Nuxt

📜 Custom Error Pages in Nuxt

Custom error pages are a great way to give your users a better experience when something goes wrong.

In this article, we'll go over how to create them in Nuxt.

Check it out here: Custom Error Pages in Nuxt

📅 Upcoming Events

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

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. I will be speaking on Nuxt Layers!

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

💬 Less Documentation

"The best reaction to "this is confusing, where are the docs" is to rewrite the feature to make it less confusing, not write more docs." — Jeff Atwood

🧠 Spaced-repetition: Hybrid API: Composition API + Options API

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 don't have to decide between Options API and Composition API, you can use both:

export default {    setup() {      const darkMode = ref(false);      return { darkMode }    },    methods: {      saveDarkMode() {        localStorage.setItem('dark-mode', this.darkMode);      },    }  };

We can also update values from the Options API:

export default {    setup() {      const darkMode = ref(false);      return { darkMode }    },    methods: {      changeTheme(val) {        this.darkMode = val;      }    }  };

Although you can access Composition API from the Options API, it's a one-way street. The Composition API cannot access anything defined through the Options API:

export default {    setup() {      const darkMode = ref(false);      // We can't access the method      this.changeTheme(true);      return { darkMode }    },    methods: {      changeTheme(val) {        this.darkMode = val;      }    }

This can be useful for incremental adoption of the Composition API, because it lets you use reusable composables in your Options API.

But mixing two styles of writing components is likely to cause more headaches than it solves, so please think twice before doing this!

🔗 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

评论

此博客中的热门博文

🔥 (#155) A Vue podcast?

Scripting News: Tuesday, February 13, 2024