9 Vue Patterns You Should Use More Often

- Andrés Cruz

ES En español

9 Vue Patterns You Should Use More Often

Vue.js has established itself as a top-tier JavaScript framework, competing directly with giants like Angular and React. Its success lies in its progressive nature, a friendly learning curve, and exceptional performance.

Designed to build interactive and dynamic web applications, Vue offers an ecosystem rich in tools and patterns. In this post, we will explore nine Vue patterns that, if incorporated into your workflow, will improve the quality, performance, and maintainability of your projects.

1. Efficient Static Components with “v-once”

When you have parts of your interface that will not change after the initial render, the v-once directive is your best ally. By using it, you tell Vue that a component or an element and all its children should only be rendered once. This means Vue will skip future updates for that portion of the DOM, resulting in improved performance, especially in large applications with a lot of static content.

<template>
  <div v-once>
    <h1>Static Title</h1>
    <p>This content will not change, so Vue does not need to check it again.</p>
  </div>
</template>

2. Robust Communication with Custom Events

Communication between components is fundamental in Vue. While "props" are excellent for passing data from parents to children, custom events ($emit) are the ideal solution for reverse communication (from child to parent). This pattern decouples components, allowing a child component to notify the parent about an occurrence without needing to know the parent's implementation details.

<!-- In the child component (e.g., AlertButton.vue) -->
<button @click="$emit('alert', 'This is a message from the child')">Trigger Event</button>
<!-- In the parent component -->
<template>
  <AlertButton @alert="showMessage" />
</template>
<script>
export default {
  methods: {
    showMessage(message) {
      alert(message);
    }
  }
}
</script>

3. Smart Use of “v-if” vs. “v-show”

Both directives control the visibility of elements, but in very different ways.

  • v-if: It is "lazy." It renders the block only if the condition is true. If the condition changes to false, the element and its child components are destroyed and removed from the DOM. It is ideal for content that rarely needs to be shown or for controlling the rendering of heavy components.
  • v-show: It always renders the element in the DOM and simply toggles its visibility by changing the CSS display property. It is more performant for elements that are shown and hidden frequently, as the cost of toggling CSS is lower than creating and destroying DOM elements.

The general rule is: use v-if if the condition does not change frequently, and v-show if you need to toggle visibility often.

4. Logic Reuse with "Composition API" (formerly Mixins)

In Vue 2, Mixins were the primary way to reuse logic between components. However, they could lead to issues like name collisions and difficulty tracing where a property originated. Vue 3 introduced the Composition API, a much cleaner, more flexible, and scalable way to organize and reuse logic.

With the Composition API, you can encapsulate reactive logic into functions called "composables," which can be imported and used in any component.

// composables/useCounter.js
import { ref } from 'vue';
export function useCounter() {
  const counter = ref(0);
  function increment() {
    counter.value++;
  }
  return { counter, increment };
}
// In a component
<script setup>
import { useCounter } from './composables/useCounter';
const { counter, increment } = useCounter();
</script>
<template>
  <button @click="increment">Counter: {{ counter }}</button>
</template>

5. Dependency Injection with Provide/Inject

When you need to pass data from a parent component to a deeply nested descendant, "prop drilling" (passing props through multiple levels) can become tedious. provide and inject solve this problem. An ancestor component can "provide" data or functions that any descendant component in its hierarchy can "inject" and use directly.

// Ancestor Component (e.g., App.vue)
import { provide, ref } from 'vue';
export default {
  setup() {
    const theme = ref('light');
    provide('theme', theme); // Providing a reactive reference
    function toggleTheme() {
      theme.value = theme.value === 'light' ? 'dark' : 'light';
    }
    provide('toggleTheme', toggleTheme);
  }
}
// Descendant Component (e.g., DeepButton.vue)
import { inject } from 'vue';
export default {
  setup() {
    const theme = inject('theme'); // Injecting the theme
    const toggleTheme = inject('toggleTheme'); // Injecting the function
    return { theme, toggleTheme };
  }
}

6. Two-Way Synchronization with “v-model”

The v-model directive is the cornerstone of form handling in Vue. It creates a two-way data binding between a form element (like an input, textarea, or select) and a variable in your component's state. This means any change in the form field updates the variable, and any change in the variable updates the form field value, automatically keeping everything in sync.

<template>
  <input v-model="message" placeholder="Write something" />
  <p>The message is: {{ message }}</p>
</template>
<script>
export default {
  data() {
    return {
      message: ''
    };
  }
};
</script>

7. List Optimization with “key”

When using v-for to render a list of elements, it is crucial to provide a unique key attribute for each element. The key helps Vue identify each node uniquely, allowing it to efficiently reuse and reorder elements when the list changes. Using the array index as a key is an anti-pattern if the list can be reordered, deleted, or have elements inserted in the middle, as it can lead to subtle bugs and poor performance. Always use a unique and stable ID from the element itself.

<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.text }}
    </li>
  </ul>
</template>

8. Flexible Components with Slots

"Slots" are a powerful feature for creating reusable and flexible components. They allow a parent component to insert content into a child component's template. This is perfect for creating layout components like cards, modals, or layouts that need a base structure but with variable content.

<!-- Child component (e.g., BaseCard.vue) -->
<template>
  <div class="card">
    <div class="card-header">
      <slot name="header">Default Title</slot>
    </div>
    <div class="card-body">
      <slot>Default content.</slot>
    </div>
  </div>
</template>
<!-- Parent component using BaseCard.vue -->
<BaseCard>
  <template v-slot:header>
    <h2>My Custom Title</h2>
  </template>
  
  <p>This is the main content of my card.</p>
</BaseCard>

9. Computed Properties for Derived Data

Computed properties are one of the most important and useful concepts in Vue. They allow you to define a property that is calculated from other properties in the state. Vue caches the result of a computed property and only recalculates it when one of its reactive dependencies has changed. This is much more efficient than having a function in the template, which would execute on every render.

They are perfect for manipulating data, filtering lists, or any value that depends on another.

<script>
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    };
  },
  computed: {
    fullName() {
      // This will automatically recalculate if 'firstName' or 'lastName' change
      return `${this.firstName} ${this.lastName}`;
    }
  }
};
</script>
<template>
  <p>Welcome, {{ fullName }}</p>
</template>

Conclusion

Mastering these patterns will not only make you a more efficient Vue developer but will also improve the structure, performance, and scalability of your applications. By applying v-once for static content, using the Composition API for reusable logic, and choosing wisely between v-if and v-show, you will be on the right track to building high-quality Vue applications.

Discover 9 Vue.js patterns that will improve your web projects. Learn about components, communication, forms, and more. Optimize your development with Vue!

I agree to receive announcements of interest about this Blog.

Andrés Cruz

ES En español