javascriptroom guide

The Comprehensive Vue.js Tutorial for New Developers

Vue.js (often called Vue) is a progressive JavaScript framework for building user interfaces. Unlike monolithic frameworks that dictate every aspect of your project, Vue is designed to be incrementally adoptable—you can start with just a few features and scale up as needed. Its gentle learning curve, reactivity system, and component-based architecture make it a favorite among developers, from beginners to experts. If you’re new to Vue (or even web development), this tutorial will guide you through the fundamentals and beyond. By the end, you’ll have the skills to build interactive, modern web applications with Vue.js. We’ll cover setup, core concepts, components, routing, state management, and deployment—all with hands-on examples. **Prerequisites**: Basic knowledge of HTML, CSS, and JavaScript (ES6+ recommended) will help, but no prior Vue experience is required.

Table of Contents

  1. Getting Started with Vue.js
  2. The Vue Instance
  3. Vue Templates: HTML with Superpowers
  4. Reactivity: The Heart of Vue
  5. Components: Building Blocks of Vue Apps
  6. Props and Events: Communication Between Components
  7. Lifecycle Hooks: Understanding Vue’s Lifecycle
  8. Vue Router: Adding Navigation
  9. State Management with Pinia
  10. Deploying Your Vue App
  11. Conclusion
  12. References

1. Getting Started with Vue.js

Before diving into code, let’s set up your Vue development environment. Vue offers multiple installation methods to suit different needs:

Option 1: CDN (Quick Prototyping)

For small projects or learning, you can include Vue directly via a CDN. Add this script to your HTML file:

<!-- Vue 3 (latest version) -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

Now you can start using Vue in a single HTML file. Try this minimal example:

<!DOCTYPE html>
<html>
<head>
  <title>Vue CDN Example</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
  <div id="app">
    {{ message }}
  </div>

  <script>
    const { createApp } = Vue;

    createApp({
      data() {
        return {
          message: "Hello, Vue!"
        }
      }
    }).mount('#app');
  </script>
</body>
</html>

Open this file in a browser, and you’ll see “Hello, Vue!”—your first Vue app!

Option 2: Vue CLI (Legacy, but Still Used)

Vue CLI is a command-line tool for scaffolding Vue projects. While it’s been superseded by Vite, it’s still widely used.

Setup:

  1. Install Node.js (v14.0+ recommended) from nodejs.org.
  2. Install Vue CLI globally:
    npm install -g @vue/cli
  3. Create a project:
    vue create my-vue-app
  4. Follow the prompts to select features (e.g., Babel, TypeScript, Vue Router).

Vite is a next-gen build tool that’s faster and more efficient than Vue CLI. It’s the official recommendation for Vue 3.

Setup:

  1. Ensure Node.js (v14.18+ or v16+) is installed.
  2. Create a new Vue project with Vite:
    npm create vue@latest
  3. Answer the prompts (name your project, enable TypeScript, Vue Router, Pinia, etc.).
  4. Navigate to the project folder and install dependencies:
    cd my-vue-app
    npm install
  5. Run the development server:
    npm run dev

Your app will be live at http://localhost:5173!

2. The Vue Instance

Every Vue app starts with a Vue instance (or “app instance” in Vue 3). This instance connects your data and logic to the DOM.

Creating an App Instance

In Vue 3, you use createApp() to initialize an app:

import { createApp } from 'vue';
import App from './App.vue'; // Root component

const app = createApp(App);
app.mount('#app'); // Mounts the app to the DOM element with id="app"

The mount('#app') method attaches the app to the HTML element <div id="app"></div>.

App Options: Data, Methods, and More

The app instance accepts options to define behavior. The most common are:

  • data(): Returns an object of reactive data properties.
  • methods: Object of functions to handle logic.

Example:

<!-- In your HTML -->
<div id="app">
  <p>{{ count }}</p>
  <button @click="increment">Increment</button>
</div>

<script>
  const { createApp } = Vue;

  createApp({
    data() {
      return {
        count: 0 // Reactive data property
      };
    },
    methods: {
      increment() {
        this.count++; // "this" refers to the app instance
      }
    }
  }).mount('#app');
</script>

Here, count is reactive: when it changes, the DOM updates automatically. The increment method modifies count, and @click (shorthand for v-on:click) triggers the method.

3. Vue Templates: HTML with Superpowers

Vue templates extend HTML with special syntax to bind data and logic. Let’s explore key features:

Text Interpolation

Use double curly braces {{ }} to insert data into the DOM:

<div>{{ message }}</div>
data() {
  return {
    message: "Hello, Vue!" // Renders as "Hello, Vue!"
  };
}

Directives: Special HTML Attributes

Directives are prefixed with v- and apply reactive behavior to the DOM.

v-bind: Dynamic Attributes

Bind HTML attributes to data with v-bind: (shorthand :):

<img v-bind:src="imageUrl" alt="Vue Logo">
<!-- Shorthand -->
<img :src="imageUrl" alt="Vue Logo">
data() {
  return {
    imageUrl: "https://vuejs.org/images/logo.png"
  };
}

v-on: Event Handling

Listen to DOM events with v-on: (shorthand @):

<button v-on:click="greet">Say Hello</button>
<!-- Shorthand -->
<button @click="greet">Say Hello</button>
methods: {
  greet() {
    alert("Hello!");
  }
}

v-model: Two-Way Binding

v-model creates two-way binding between form inputs and data:

<input v-model="name" placeholder="Enter your name">
<p>Hello, {{ name }}!</p>
data() {
  return {
    name: "" // Updates as the user types
  };
}

v-for: List Rendering

Loop through arrays with v-for:

<ul>
  <li v-for="(item, index) in items" :key="index">
    {{ index + 1 }}. {{ item }}
  </li>
</ul>
data() {
  return {
    items: ["Apple", "Banana", "Cherry"]
  };
}

Always use :key with v-for to help Vue track list items efficiently.

v-if / v-else: Conditional Rendering

Conditionally render elements with v-if, v-else-if, and v-else:

<p v-if="score >= 90">A+</p>
<p v-else-if="score >= 80">A</p>
<p v-else>Pass</p>
data() {
  return {
    score: 85 // Renders "A"
  };
}

4. Reactivity: The Heart of Vue

Vue’s reactivity system automatically updates the DOM when data changes. Here’s how it works:

How Reactivity Works

In Vue 3, reactivity is powered by ES6 Proxies. When you define data properties, Vue wraps them in a proxy that tracks dependencies (e.g., template expressions, computed properties) and triggers updates when the data changes.

Key Rule: Declare Data Upfront

For reactivity to work, data properties must be declared in the data() function. Adding new properties later won’t trigger updates.

Bad:

data() {
  return {}; // Empty data
}

// Later...
this.newProperty = "Hello"; // Not reactive!

Good:

data() {
  return {
    newProperty: null // Declare upfront
  };
}

// Later...
this.newProperty = "Hello"; // Reactive!

Example: Reactive Counter

<div id="app">
  <p>Count: {{ count }}</p>
  <button @click="increment">+</button>
  <button @click="decrement">-</button>
</div>

<script>
  const { createApp } = Vue;

  createApp({
    data() {
      return { count: 0 };
    },
    methods: {
      increment() { this.count++; },
      decrement() { this.count--; }
    }
  }).mount('#app');
</script>

Clicking the buttons updates count, and Vue automatically re-renders the {{ count }} interpolation.

5. Components: Building Blocks of Vue Apps

Components are reusable, self-contained units that encapsulate HTML, CSS, and JavaScript. They let you split your app into manageable parts.

Component Structure

A Vue component typically has three sections:

  • <template>: HTML markup.
  • <script>: Logic (data, methods, etc.).
  • <style>: CSS (scoped to the component by adding scoped).

Global vs. Local Components

  • Global Components: Registered once and usable anywhere in the app.
  • Local Components: Registered only in the parent component that needs them.

Example: Global Component

Register a global component in your main app file:

// main.js
import { createApp } from 'vue';
import App from './App.vue';
import Greeting from './components/Greeting.vue'; // Import component

const app = createApp(App);
app.component('Greeting', Greeting); // Register globally
app.mount('#app');

Now use it in any template:

<!-- In any component -->
<Greeting />

Example: Local Component

Register a component only in the parent:

<!-- ParentComponent.vue -->
<template>
  <div>
    <LocalGreeting /> <!-- Use local component -->
  </div>
</template>

<script>
import LocalGreeting from './LocalGreeting.vue'; // Import

export default {
  components: {
    LocalGreeting // Register locally
  }
};
</script>

Single-File Components (SFCs)

SFCs (.vue files) are the standard way to write Vue components. Here’s a simple Greeting.vue:

<!-- Greeting.vue -->
<template>
  <h1>Hello, {{ name }}!</h1>
</template>

<script>
export default {
  data() {
    return {
      name: "Vue Developer"
    };
  }
};
</script>

<style scoped>
h1 {
  color: blue;
}
</style>

The scoped attribute in <style> ensures CSS only applies to this component.

6. Props and Events: Communication Between Components

Components need to communicate. Props send data from parent to child, and events send data from child to parent.

Props: Parent → Child

Props are custom attributes for passing data to child components.

Step 1: Define Props in Child Component

<!-- ChildComponent.vue -->
<template>
  <p>Parent says: {{ message }}</p>
</template>

<script>
export default {
  props: {
    message: {
      type: String, // Validate prop type
      required: true // Make it required
    }
  }
};
</script>

Step 2: Pass Props from Parent

<!-- ParentComponent.vue -->
<template>
  <ChildComponent message="Hello from Parent!" />
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: { ChildComponent }
};
</script>

Events: Child → Parent

Children emit events to send data to parents using this.$emit(eventName, data).

Step 1: Emit Event in Child

<!-- ChildComponent.vue -->
<template>
  <button @click="sendMessage">Send to Parent</button>
</template>

<script>
export default {
  methods: {
    sendMessage() {
      this.$emit('child-message', 'Hello from Child!'); // Emit event
    }
  }
};
</script>

Step 2: Listen to Event in Parent

<!-- ParentComponent.vue -->
<template>
  <ChildComponent @child-message="handleMessage" />
  <p>Child said: {{ childMessage }}</p>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: { ChildComponent },
  data() {
    return { childMessage: "" };
  },
  methods: {
    handleMessage(message) {
      this.childMessage = message; // Update parent data
    }
  }
};
</script>

7. Lifecycle Hooks: Understanding Vue’s Lifecycle

Vue components go through a series of stages from creation to destruction. Lifecycle hooks let you run code at specific stages.

Common Lifecycle Hooks

HookWhen It RunsUse Case Example
beforeCreateBefore the component is initialized.Rarely used.
createdAfter data and methods are initialized.Fetch initial data from an API.
beforeMountBefore the component is mounted to the DOM.Prepare data for rendering.
mountedAfter the component is mounted to the DOM.Access DOM elements, start timers.
beforeUpdateBefore data changes cause a re-render.Clean up before updates.
updatedAfter data changes and re-render.Perform post-update DOM operations.
beforeUnmountBefore the component is removed from the DOM.Clean up timers, event listeners.
unmountedAfter the component is removed.Final cleanup.

Example: Using mounted to Fetch Data

<template>
  <ul>
    <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
  </ul>
</template>

<script>
export default {
  data() {
    return { posts: [] };
  },
  mounted() {
    // Fetch data when component is mounted
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(res => res.json())
      .then(data => this.posts = data.slice(0, 5)); // Show first 5 posts
  }
};
</script>

8. Vue Router: Adding Navigation

Vue Router is the official routing library for Vue apps, enabling single-page application (SPA) navigation.

Setup with Vite

If you used npm create vue@latest, select “Vue Router” during setup. Otherwise:

npm install vue-router@4

Basic Routing Example

Step 1: Define Routes

Create src/router/index.js:

import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';

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

const router = createRouter({
  history: createWebHistory(), // Uses HTML5 history mode
  routes
});

export default router;

Step 2: Register Router in App

In main.js:

import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // Import router

createApp(App)
  .use(router) // Use the router
  .mount('#app');

In App.vue:

<template>
  <nav>
    <!-- Router links (replace <a> tags) -->
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </nav>
  <!-- Where routed components render -->
  <router-view />
</template>

<style>
/* Style active router link */
.router-link-active {
  color: blue;
  font-weight: bold;
}
</style>

Dynamic Routes

Use :param in the path to match dynamic values (e.g., user IDs):

// router/index.js
{ path: '/user/:id', name: 'User', component: User }

Access the parameter in the component:

<!-- User.vue -->  
<template>
  <p>User ID: {{ $route.params.id }}</p>
</template>

9. State Management with Pinia

Pinia is Vue’s official state management library (replaces Vuex). Use it to share data across components.

Setup with Vite

Select “Pinia” during npm create vue@latest setup, or install manually:

npm install pinia

Example: Counter Store

Step 1: Create a Store

Create src/stores/counter.js:

import { defineStore } from 'pinia';

// Define a store with id "counter"
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }), // Reactive state
  getters: {
    doubleCount: (state) => state.count * 2 // Computed-like values
  },
  actions: {
    increment() { state.count++; }, // Methods to modify state
    decrement() { state.count--; }
  }
});

Step 2: Use the Store in Components

<!-- AnyComponent.vue -->
<template>
  <p>Count: {{ counterStore.count }}</p>
  <p>Double: {{ counterStore.doubleCount }}</p>
  <button @click="counterStore.increment">+</button>
  <button @click="counterStore.decrement">-</button>
</template>

<script>
import { useCounterStore } from '../stores/counter';

export default {
  setup() {
    const counterStore = useCounterStore();
    return { counterStore };
  }
};
</script>

10. Deploying Your Vue App

Once your app is ready, build it for production and deploy it to a hosting service.

Build for Production

Run the build command to generate optimized static files:

npm run build

This creates a dist/ folder with HTML, CSS, and JS files.

Deploy Options

  • Netlify: Connect your GitHub repo, set build command npm run build, and publish directory dist.
  • Vercel: Similar to Netlify; import your repo and deploy.
  • GitHub Pages: Push the dist/ folder to a gh-pages branch (use gh-pages package for automation).

Example: Deploy to Netlify

  1. Push your code to GitHub.
  2. Go to Netlify and import your repo.
  3. Set build settings:
    • Build command: npm run build
    • Publish directory: dist
  4. Click “Deploy”—your app will be live!

11. Conclusion

You’ve now learned the fundamentals of Vue.js: from setting up your project and creating components to routing, state management, and deployment. Vue’s simplicity and flexibility make it a great choice for building everything from small interactive widgets to large-scale SPAs.

To continue your journey:

  • Explore the Vue 3 Documentation.
  • Learn the Composition API (a more flexible alternative to the Options API used here).
  • Dive into testing with Vue Test Utils.
  • Join the Vue community on Discord or Reddit.

12. References

Happy coding! 🚀