Creating Single Page Applications (SPAs) with Vue.js — A Complete Beginner’s Guide

957 0 0 0 0

📘 Chapter 4: Building Reusable Components for Your SPA

🧭 What You’ll Learn

In this chapter, you'll master how to:

  • Understand the benefits of reusable components in Vue
  • Create and organize components efficiently
  • Pass props and emit events for communication
  • Use slots for flexible layouts
  • Apply global and local component registration
  • Build real-world reusable components like Buttons, Cards, and Layouts

🧱 What Are Components in Vue?

Vue components are self-contained, reusable UI elements that help you break down a complex application into modular parts.

Each component can contain:

  • Template: HTML markup
  • Script: Component logic (JS)
  • Style: Scoped or global CSS

🔁 Benefits of Reusable Components

  • Maintainable codebase
  • Less repetition (DRY principle)
  • Easier to debug and extend
  • More scalable structure
  • Cleaner layouts through abstraction

📦 Component Structure Example

vue

 

<!-- components/MyButton.vue -->

<template>

  <button class="my-btn" @click="handleClick">

    <slot />

  </button>

</template>

 

<script>

export default {

  name: 'MyButton',

  emits: ['click'],

  methods: {

    handleClick() {

      this.$emit('click')

    }

  }

}

</script>

 

<style scoped>

.my-btn {

  padding: 10px 20px;

  background-color: #42b983;

  color: white;

  border: none;

  border-radius: 4px;

}

</style>


📁 Recommended Component Folder Structure

bash

 

src/

── components/

│   ── BaseButton.vue

│   ── Card.vue

│   ── Navbar.vue

│   └── Footer.vue

── views/

│   ── Home.vue

│   └── About.vue

🧩 Suggested Naming Convention

Type

Prefix

Example

Base elements

Base

BaseButton.vue

Layouts

Layout

LayoutHeader.vue

Views

None

Home.vue

Modules

Functional

PostCard.vue


🛠️ Registering Components

A. Local Registration (Preferred)

vue

 

<script>

import BaseButton from './components/BaseButton.vue'

 

export default {

  components: {

    BaseButton

  }

}

</script>

B. Global Registration

main.js:

js

 

import BaseButton from './components/BaseButton.vue'

app.component('BaseButton', BaseButton)


🎯 Using Props

Props allow components to accept dynamic input from parent components.

Parent.vue:

vue

 

<UserCard :name="'Alice'" :age="30" />

UserCard.vue:

vue

 

<script>

export default {

  props: {

    name: String,

    age: Number

  }

}

</script>


🔁 Emitting Events

Child components can communicate back using $emit.

Child.vue:

vue

 

<template>

  <button @click="$emit('increment')">Add</button>

</template>

Parent.vue:

vue

 

<Counter @increment="count++" />


🎨 Using Slots

Slots allow you to inject custom content into a component’s template.

BaseCard.vue:

vue

 

<template>

  <div class="card">

    <slot />

  </div>

</template>

Usage:

vue

 

<BaseCard>

  <h3>Card Title</h3>

  <p>Some custom content inside the card.</p>

</BaseCard>


📚 Building Common Reusable Components


🟢 BaseButton.vue

vue

 

<template>

  <button :class="btnClass" @click="$emit('click')">

    <slot />

  </button>

</template>

 

<script>

export default {

  props: {

    variant: {

      type: String,

      default: 'primary'

    }

  },

  computed: {

    btnClass() {

      return `btn-${this.variant}`

    }

  }

}

</script>

 

<style scoped>

.btn-primary { background: #42b983; color: white; }

.btn-secondary { background: #ccc; color: #333; }

</style>


🟡 Card.vue

vue

 

<template>

  <div class="card">

    <slot name="header" />

    <slot />

    <slot name="footer" />

  </div>

</template>

 

<style scoped>

.card {

  border: 1px solid #eee;

  border-radius: 6px;

  padding: 20px;

  margin: 10px 0;

}

</style>


🔵 Navbar.vue

vue

 

<template>

  <nav>

    <router-link to="/">Home</router-link>

    <router-link to="/about">About</router-link>

  </nav>

</template>

 

<style scoped>

nav {

  background: #f0f0f0;

  padding: 10px;

}

nav a {

  margin-right: 10px;

  text-decoration: none;

}

</style>


🧬 Scoped vs Global Styles

Type

Usage

Scope

Scoped

<style scoped> in .vue files

Affects only component

Global

App.vue, main.css, or public/index.html

Affects entire app


🔄 Lifecycles for Reusable Components

Use lifecycle hooks like:

  • mounted(): DOM loaded
  • beforeUnmount(): clean-up logic
  • watch(): react to prop changes

🧪 Example: Watching Prop

vue

 

<script>

export default {

  props: ['count'],

  watch: {

    count(newVal) {

      console.log('Count updated:', newVal)

    }

  }

}

</script>


📊 Feature Comparison Table: Props, Events, Slots

Feature

Purpose

Use Case Example

Props

Pass data into component

:title="Hello"

Events

Emit action to parent

@click="doSomething"

Slots

Insert content into component

<slot name="header">...</slot>


Best Practices

  • Use PascalCase for component names
  • Keep components focused (1 task)
  • Limit prop count to essential inputs
  • Document props and slots in comments
  • Use default props and type validation

💡 Real-World Use Cases

Component

Usage Scenario

<BaseButton>

Action buttons with different styles

<Card>

Reusable content wrappers

<Modal>

Confirmation dialogs, form popups

<Navbar>

App navigation layout

<Toast>

Temporary feedback messages


Component Checklist


Task

Done

Component created using .vue

Props defined and validated

Events emitted as needed

Slots used for flexibility

Scoped styles applied

File organized in /components

Back

FAQs


❓1. What is a Single Page Application (SPA)?

Answer:
A Single Page Application is a web app that loads a single HTML file and dynamically updates the content without refreshing the page. This allows for a smoother user experience, similar to desktop or mobile apps.

❓2. Why should I use Vue.js to build an SPA?

Answer:
Vue.js is lightweight, beginner-friendly, and has a powerful ecosystem. It supports component-based architecture and works seamlessly with Vue Router for SPA navigation. It’s a great choice for fast, reactive, and scalable SPAs.

❓3. What are the essential tools needed to build a Vue.js SPA?

Answer:

  • Vue CLI or Vite (project scaffolding)
  • Vue Router (for client-side navigation)
  • Pinia or Vuex (for state management)
  • Single File Components (.vue)
  • Axios or Fetch API (for server communication, if needed)

❓4. How does routing work in a Vue.js SPA?

Answer:
Vue Router handles navigation in the browser without reloading pages. It maps URL paths to Vue components and updates the view dynamically using the browser’s History API.

❓5. Can I use Vue.js without Vue Router for an SPA?

Answer:
Technically yes, but it’s not recommended. Vue Router provides essential SPA features like URL mapping, history handling, navigation guards, and lazy loading.

❓6. What is the difference between Vuex and Pinia?

Answer:
Both are state management libraries. Vuex is the older, more complex option, while Pinia is the official replacement for Vue 3—lighter, easier to use, and modular.

❓7. Can I build an SPA with Vue using only CDN links?

Answer:
Yes, but it's only suitable for small-scale SPAs. For larger projects, using the Vue CLI or Vite offers better file organization, hot reloading, and tooling support.

❓8. How do I handle SEO with a Vue SPA?

Answer:
SPAs typically have poor SEO out of the box because content is rendered via JavaScript. Use pre-rendering (e.g., with Prerender.io) or switch to Server-Side Rendering (SSR) with Nuxt.js for better SEO.

❓9. What hosting options are available for deploying Vue SPAs?

Answer:
You can deploy your Vue SPA on:

  • Netlify (easy integration with Git)
  • Vercel
  • Firebase Hosting
  • GitHub Pages
  • Render or DigitalOcean App Platform

❓10. Is Vue.js suitable for large-scale Single Page Applications?

Answer:
Yes. Vue.js is modular and scalable. With features like lazy loading, component splitting, Pinia/Vuex for state management, and Vue Router, it can power large, production-ready SPAs effectively.