💚 Vue.js — Zero to Hero
The progressive JavaScript framework — approachable, performant, versatile
💚 What is Vue.js?
Vue is a progressive JavaScript framework for building UIs. It's designed to be adoptable incrementally — use it for a widget or build a full SPA.
📄 SFC
Single File Components keep template, script, and style in one .vue file.
🔄 Reactivity
Vue's reactivity system automatically tracks and updates the DOM.
🧩 Composition API
Modern, flexible way to organize component logic.
🌍 Ecosystem
Pinia for state, Vue Router for routing, Nuxt for SSR.
⚙️ Setup
# Create new Vue app (Vite) npm create vue@latest my-app cd my-app npm install npm run dev # → http://localhost:5173
Project structure:
my-app/ ├── src/ │ ├── App.vue # Root component │ ├── main.js # Entry point │ ├── components/ # Reusable components │ ├── views/ # Page components │ └── stores/ # Pinia stores └── vite.config.js
📄 Single File Component (SFC)
Every .vue file has three blocks: <template>, <script>, and <style>.
<template> <div class="card"> <h1>{{ title }}</h1> <p>{{ description }}</p> </div> </template> <script setup> const title = 'Hello Vue!'; const description = 'This is my first component'; </script> <style scoped> /* scoped = only this component */ .card { padding: 1rem; border-radius: 8px; } </style>
🔄 Reactivity — ref & reactive
<script setup> import { ref, reactive } from 'vue'; // ref — for primitives const count = ref(0); const name = ref('Alice'); // reactive — for objects const user = reactive({ name: 'Bob', age: 25, }); function increment() { count.value++; // use .value in script user.age++; } </script> <template> <p>{{ count }}</p> <!-- no .value in template --> <button @click="increment">+</button> </template>
🧮 Computed Properties
<script setup> import { ref, computed } from 'vue'; const price = ref(100); const quantity = ref(3); // Auto-recalculates when price or quantity changes const total = computed(() => price.value * quantity.value); const items = ref(['apple', 'banana', 'cherry']); const filtered = computed(() => items.value.filter(i => i.includes('a')) ); </script> <template> <p>Total: ${{ total }}</p> </template>
👁 Watchers
<script setup> import { ref, watch, watchEffect } from 'vue'; const search = ref(''); // watch — specific source watch(search, (newVal, oldVal) => { console.log('Search changed:', newVal); }); // watchEffect — auto-tracks dependencies watchEffect(() => { console.log('Current search:', search.value); }); </script>
🏷 Template Directives
| Directive | Purpose |
|---|---|
v-if / v-else | Conditional rendering |
v-show | Toggle visibility (CSS display) |
v-for | Loop over arrays/objects |
v-model | Two-way data binding |
v-bind / : | Bind attribute dynamically |
v-on / @ | Listen to events |
<template> <p v-if="isLoggedIn">Welcome back!</p> <p v-else>Please log in</p> <li v-for="item in items" :key="item.id">{{ item.name }}</li> <input v-model="search" /> <!-- two-way bind --> <img :src="imageUrl" /> <!-- dynamic attr --> <button @click="doSomething">Go</button> </template>
🖱 Events
<template> <!-- Modifiers --> <form @submit.prevent="onSubmit"> <!-- preventDefault --> <a @click.stop="onClick"> <!-- stopPropagation --> <input @keyup.enter="onEnter" /> <!-- key filter --> <button @click.once="onClick"> <!-- fire once --> </template>
📨 Props & Emits
<!-- Child.vue --> <script setup> const props = defineProps({ title: { type: String, required: true }, count: { type: Number, default: 0 }, active: { type: Boolean, default: false }, }); const emit = defineEmits(['update', 'close']); function handleClick() { emit('update', props.count + 1); } </script> <!-- Parent.vue --> <Child :title="'Hello'" :count="num" @update="num = $event" />
♻️ Lifecycle Hooks
<script setup> import { onMounted, onUpdated, onUnmounted, onBeforeMount } from 'vue'; onMounted(() => console.log('Component mounted')); // ← most common onUpdated(() => console.log('Component updated')); onUnmounted(() => console.log('Component destroyed')); // cleanup here </script>
🧩 Composables
Composables are reusable functions that use Vue's reactivity — like React custom hooks.
// composables/useFetch.js import { ref } from 'vue'; export function useFetch(url) { const data = ref(null); const loading = ref(true); const error = ref(null); fetch(url) .then(r => r.json()) .then(d => data.value = d) .catch(e => error.value = e) .finally(() => loading.value = false); return { data, loading, error }; } // Usage in any component const { data, loading } = useFetch('/api/users');
🍍 Pinia (State Management)
# Install
npm install pinia
// stores/counter.js import { defineStore } from 'pinia'; import { ref, computed } from 'vue'; export const useCounterStore = defineStore('counter', () => { const count = ref(0); const double = computed(() => count.value * 2); function increment() { count.value++; } return { count, double, increment }; }); // Use in any component <script setup> const counter = useCounterStore(); </script> <template> <p>{{ counter.count }}</p> <button @click="counter.increment">+</button> </template>
🔗 Vue Router
# Install
npm install vue-router
// router/index.js import { createRouter, createWebHistory } from 'vue-router'; const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', component: () => import('../views/Home.vue') }, { path: '/about', component: () => import('../views/About.vue') }, { path: '/post/:id', component: () => import('../views/Post.vue') }, ], }); // In a component import { useRouter, useRoute } from 'vue-router'; const router = useRouter(); const route = useRoute(); console.log(route.params.id); router.push('/about');
🚀 Pro Tips
<!-- v-model on custom components --> <MyInput v-model="username" /> <!-- Slots — pass template content to child --> <Card> <template #header><h2>Title</h2></template> <p>Body content here</p> </Card> <!-- Teleport — render outside current DOM tree --> <Teleport to="body"> <Modal v-if="showModal" /> </Teleport> <!-- Transition animation --> <Transition name="fade"> <p v-if="show">Fades in/out</p> </Transition>
🎉 You now know Vue.js! Next steps: learn Nuxt.js for SSR/SSG, Pinia for complex state, and the Vueuse library for useful composables.