Top 10 Mistakes in Vue.js and How to Fix Them

Vue.js is one of the most popular front-end JavaScript frameworks, offering a reactive data-binding system and component-based architecture. However, developers—especially beginners—often make mistakes that can lead to performance issues, difficult debugging, and security risks.

In this article, we’ll explore the top 10 mistakes in Vue.js and how to fix them with best practices and code examples.

1️⃣ Not Using key in v-for Loops

Mistake: Not Assigning a Unique Key in v-for

<li v-for="item in items">{{ item.name }}</li>

Issue:

  • Vue re-renders the entire list when data changes, reducing performance.

Solution: Use a Unique key in v-for Loops

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

Benefit: Optimizes list rendering by ensuring Vue tracks each item correctly.

2️⃣ Modifying Props Directly Inside Child Components

Mistake: Changing Props in a Child Component

<script setup>
defineProps(['message']);
message = "New message"; // ❌ Mutating prop directly
</script>

Issue:

  • Props should be read-only, and modifying them breaks Vue’s reactivity system.

Solution: Use computed Properties or Emit Events

<script setup>
defineProps(['message']);
const updatedMessage = computed(() => message + " Updated!");
</script>

Benefit: Preserves reactivity and ensures proper data flow.

3️⃣ Using v-if and v-for Together

Mistake: Using v-if and v-for in the Same Element

<li v-for="item in items" v-if="item.show">{{ item.name }}</li>

Issue:

  • Vue evaluates v-for first, iterating over all elements before filtering, reducing efficiency.

Solution: Use computed to Filter the List Before Rendering

<template>
  <li v-for="item in filteredItems" :key="item.id">{{ item.name }}</li>
</template>

<script setup>
const filteredItems = computed(() => items.filter(item => item.show));
</script>

Benefit: Improves performance by filtering items before rendering.

4️⃣ Not Cleaning Up Event Listeners

Mistake: Adding Event Listeners Without Removing Them

mounted() {
  window.addEventListener("scroll", this.handleScroll);
}

Issue:

  • Memory leaks occur if listeners are not removed when the component is destroyed.

Solution: Remove Event Listeners in onUnmounted

onMounted(() => window.addEventListener("scroll", handleScroll));
onUnmounted(() => window.removeEventListener("scroll", handleScroll));

Benefit: Prevents memory leaks and ensures better performance.

5️⃣ Directly Modifying ref Objects Instead of value

Mistake: Changing ref Object Instead of value

const counter = ref(0);
counter = 5; // ❌ Reassigning a ref object

Issue:

  • Vue does not detect changes if you reassign the entire ref object.

Solution: Modify the .value Property

const counter = ref(0);
counter.value = 5; // ✅ Updates correctly

Benefit: Ensures reactivity is preserved.

6️⃣ Not Using Vue DevTools for Debugging

Mistake: Debugging Without Vue DevTools

console.log(data); // ❌ Not efficient for debugging

Issue:

  • Harder to inspect reactivity issues and Vuex state changes.

Solution: Use Vue DevTools

  • Install Vue DevTools from here.
  • Inspect components, Vuex state, and events in real-time.

Benefit: Easier debugging and performance monitoring.

7️⃣ Using Global Variables Instead of Vuex/Pinia

Mistake: Storing Global Data in window or Local Storage

window.globalUser = { name: "John" }; // ❌ Not reactive

Issue:

  • No reactivity, and changes do not update the UI.

Solution: Use Vuex or Pinia for State Management

import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({ name: "John" }),
});

Benefit: Maintains reactive, centralized state management.

8️⃣ Not Lazy Loading Routes

Mistake: Loading All Routes Eagerly

import Home from './views/Home.vue';
import About from './views/About.vue';

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About }
];

Issue:

  • Increases initial bundle size, slowing down page loads.

Solution: Use Lazy Loading with defineAsyncComponent

const Home = defineAsyncComponent(() => import('./views/Home.vue'));
const About = defineAsyncComponent(() => import('./views/About.vue'));

Benefit: Reduces initial load time and improves performance.

9️⃣ Using watch Instead of computed for Derived State

Mistake: Using watch to Compute Derived Data

const fullName = ref("");

watch(() => firstName.value + " " + lastName.value, (newVal) => {
  fullName.value = newVal;
});

Issue:

  • watch is meant for side effects, not computing new values.

Solution: Use computed Instead

const fullName = computed(() => firstName.value + " " + lastName.value);

Benefit: More efficient and automatically updates.

🔟 Not Securing API Requests

Mistake: Sending API Requests Without Authorization

axios.get('/api/users'); // ❌ No authentication token

Issue:

  • Exposes sensitive data to unauthorized users.

Solution: Attach Authorization Headers

axios.get('/api/users', {
  headers: { Authorization: `Bearer ${token}` }
});

Benefit: Ensures secure communication with APIs.

🎯 Conclusion

Vue.js is a powerful framework, but avoiding these common mistakes can help you build high-performance, secure, and maintainable applications.

Use key in v-for loops for better rendering
Avoid modifying props directly—use computed properties instead
Unsubscribe from event listeners to prevent memory leaks
Use Vuex/Pinia for state management
Lazy load routes to optimize performance
Secure API requests properly

By following these best practices, you’ll write better, more efficient Vue.js applications! 🚀

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare