The Real Vue.js Dev Journey: Wins & Woes
When I first dipped my toes into Vue.js, it was a bit of a rollercoaster. Coming from the React world, things felt… familiar but just different enough to be confusing. v-for instead of map()? ref() and reactive() instead of useState()? My brain definitely needed a minute (or several) to adjust.
But here’s the thing: once I pushed through the initial friction, Vue clicked. The more I built with it, the more I started appreciating its design choices—and eventually, it became a go-to tool in my freelance toolkit.
Today, I want to share that journey: the wins, the woes, and how Vue eventually won me over.
My Vue Setup
Let’s start with what I’m working with. My current Vue 3 stack looks like this:
- Vite – Ridiculously fast dev server
- Pinia – State management that actually makes sense
- Vue Router – Gets the job done (mostly)
- TypeScript – Because I love catching bugs before they bite
It’s a solid combo—but like any setup, it’s had its quirks. Let’s talk about some of the challenges I ran into and how I worked through them.
The Learning Curve: Vue's Reactivity System
Coming from React, I had to unlearn a lot.
In Vue, state and reactivity are handled differently. ref(), reactive(), watch(), watchEffect()—it felt like learning to speak a new dialect of JavaScript.
At first, I tried mapping everything to React equivalents. Spoiler: that didn’t work. Vue has its own philosophy, especially around reactivity.
What Helped
Honestly? Just building stuff. The more I experimented, the more things made sense. Vue’s documentation was a lifesaver, and small side projects helped me test ideas without the pressure of client deadlines.
State Management: Why Pinia Won Me Over
Ah, global state. Every dev’s favorite topic. 😅
I was a little wary when it came to managing shared state in Vue. I’d heard horror stories about Vuex, but then I discovered Pinia—and it changed everything.
It's like Vuex but… actually enjoyable to use. Here's a peek at my cart store setup:
import { defineStore } from "pinia";
export const useCartStore = defineStore("cart", { // [!code word:cart]
state: () => ({ // [!code focus]
items: [], // [!code focus]
loading: false, // [!code focus]
error: null, // [!code focus]
}), // [!code focus]
getters: { // [!code focus]
itemCount: (state) => state.items.reduce((sum, i) => sum + i.quantity, 0), // [!code focus]
totalPrice: (state) => // [!code focus]
state.items.reduce((sum, i) => sum + i.price * i.quantity, 0), // [!code focus]
}, // [!code focus]
actions: { // [!code focus]
async fetchCart() { // [!code focus]
try { // [!code focus]
this.loading = true; // [!code focus]
this.items = await fetchCartFromAPI(); // [!code focus]
} catch (err) { // [!code focus]
this.error = // [!code focus]
err instanceof Error ? err.message : "Oops, something went wrong."; // [!code focus]
} finally { // [!code focus]
this.loading = false; // [!code focus]
} // [!code focus]
}, // [!code focus]
}, // [!code focus]
});
Why I love it:
- TypeScript-friendly out of the box
- Great DX with auto-complete
- No boilerplate
Routing Woes: Dynamic Routes & Nesting
Vue Router is powerful, but I’ll be real—it took a minute to wrap my head around dynamic routes and nested navigation.
Coming from frameworks like Next.js where file-based routing is the default, manually defining routes felt… a bit dated.
How I Solved It
Here’s my typical setup:
import { createRouter, createWebHistory } from "vue-router";
import Home from "@/views/Home.vue";
import Product from "@/views/Product.vue";
const routes = [ // [!code focus]
{ path: "/", component: Home }, // [!code focus]
{ path: "/product/:id", component: Product, props: true }, // [!code focus]
]; // [!code focus]
const router = createRouter({
history: createWebHistory(),
routes, // [!code focus]
});
export default router;
It works well enough, but if Vue Router ever introduces file-based routing like Nuxt, I’d be very interested. 👀
Of course, Vue can handle file-based routing, too. It just requires a bit more setup. 🤷
Performance Tuning: Getting Snappy
When I started working with large datasets, I noticed a few hiccups—slow renders, sluggish updates, and other small annoyances.
What I Did
- Lazy loading components to improve initial load times
- Using
computedinstead ofwatchwhere it made sense - Performance tracking in dev with a simple toggle:
import { createApp } from "vue";
import App from "@/App.vue";
const app = createApp(App);
// Performance optimization // [!code focus]
app.config.performance = import.meta.env.DEV; // Only enable performance tracking in development // [!code highlight] [!code focus]
// Compiler options for better performance // [!code focus]
app.config.compilerOptions = { // [!code focus]
comments: false, // Remove comments from templates in production // [!code focus]
delimiters: ["\${", "}"], // Use alternative delimiters to avoid conflicts with other template engines // [!code focus]
whitespace: "condense", // Remove unnecessary whitespace // [!code focus]
}; // [!code focus]
These small adjustments made a huge difference—especially for client-facing apps where speed = credibility.
The Verdict: What I love (and what...?) About Vue
What I Love
- Component-based structure makes scaling a breeze
- Reactive system feels natural once you "get it"
- Pinia is a state management dream
- Vue’s ecosystem (with Vite + TypeScript) is seriously powerful
What Still Bugs Me
- Initial setup can be intimidating, especially with TypeScript + routing
- Pinia can feel like overkill for super simple apps
- Reactivity can be a bit too eager—requires finesse
Final Thoughts: Would I Use Vue Again?
You bet.
Vue has grown on me in a big way. Once I pushed past the syntax curveballs and routing quirks, I found a toolset that’s powerful, flexible, and surprisingly enjoyable to work with.
Would I still use Next.js for certain projects? Absolutely. Especially when routing simplicity is key.
But when the job calls for a fast, lightweight, reactive frontend—and the client’s already Vue-friendly? Vue 3 is a great choice.