π§© Vue.js Component Basics: Building Blocks of Your App
Imagine youβre building with LEGO blocks. Each block is a small piece, but together they create amazing things!
π― What Are Components?
Think of components like recipe cards in a kitchen. Each card tells you how to make one dish. You can use the same recipe card again and again to make the same dish multiple times!
In Vue.js:
- A component = a reusable piece of your app
- It has its own look (HTML) and behavior (JavaScript)
- You build big apps by combining small components
graph TD A["π Your App"] --> B["π¦ Header Component"] A --> C["π¦ Sidebar Component"] A --> D["π¦ Main Content"] D --> E["π¦ Card Component"] D --> F["π¦ Card Component"] D --> G["π¦ Card Component"]
π Defining a Component
There are two ways to write a component. Think of it like writing a letter - you can use a quick note or a formal letter format!
Way 1: Single-File Component (SFC)
This is the formal letter - everything organized in one .vue file:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">
Clicked {{ count }} times
</button>
</template>
Way 2: Plain JavaScript Object
This is the quick note - good for simple cases:
export default {
data() {
return { count: 0 }
},
template: `
<button @click="count++">
Clicked {{ count }} times
</button>
`
}
π‘ Pro Tip: Most developers use Single-File Components because theyβre easier to organize!
π·οΈ Component Registration
Before using a component, Vue needs to know it exists. Itβs like introducing yourself before joining a party!
There are two types of registration:
| Type | Where it works | Best for |
|---|---|---|
| Global | Everywhere in your app | Components you use often |
| Local | Only where you import it | Most components |
π Global Registration
Global = Available Everywhere
Imagine you have a favorite toy that you want in every room of your house. You tell your parents once, and now itβs available everywhere!
import { createApp } from 'vue'
import MyButton from './MyButton.vue'
const app = createApp({})
// Register globally - now use
// <MyButton> anywhere!
app.component('MyButton', MyButton)
β οΈ The Catch
Global components have two problems:
- π Extra weight - Theyβre always loaded, even if unused
- π Hard to track - You canβt see where they come from
graph TD A["π Global Component"] --> B["Page 1 β "] A --> C["Page 2 β "] A --> D["Page 3 β "] A --> E["Page 4 - Not using it π"] style E fill:#ffcccc
π Local Registration
Local = Available Only Where You Import
This is like borrowing a book from the library. You bring it to your room, use it, and it only exists in that room!
<script setup>
import ButtonCounter from './ButtonCounter.vue'
</script>
<template>
<ButtonCounter />
</template>
Thatβs it! With <script setup>, imported components are automatically available. No extra steps!
Without <script setup>
If you use the Options API, you need one more step:
import ButtonCounter from './ButtonCounter.vue'
export default {
components: {
ButtonCounter // Register here
}
}
β Why Local is Better:
- Only loads when needed (faster app!)
- Easy to see where components come from
- Better for tree-shaking (removing unused code)
π Component Naming
Names matter! Vue gives you two ways to name components:
PascalCase (Recommended)
<template>
<MyComponent />
<AnotherWidget />
</template>
kebab-case
<template>
<my-component></my-component>
<another-widget></another-widget>
</template>
π― The Rule:
- In JavaScript: Always use
PascalCase - In Templates: Use
PascalCaseORkebab-case
Vue automatically converts between them:
MyComponentβ<my-component>ButtonCounterβ<button-counter>
Why PascalCase Wins π
<!-- Easy to spot Vue components! -->
<template>
<MyButton>Click me</MyButton>
<button>Native HTML</button>
</template>
PascalCase makes Vue components stand out from regular HTML elements!
π§± Fragment Support
Fragments = Multiple Root Elements
In Vue 2, every component needed ONE wrapper element. It was like forcing every gift into ONE box!
<!-- Vue 2: This was REQUIRED π€ -->
<template>
<div>
<h1>Title</h1>
<p>Content</p>
</div>
</template>
Vue 3 changed everything! Now you can have multiple elements at the root:
<!-- Vue 3: No wrapper needed! π -->
<template>
<h1>Title</h1>
<p>Content</p>
</template>
How Attributes Work with Fragments
When a component has one root, attributes pass through automatically:
<!-- Parent uses component with class -->
<MyComponent class="fancy" />
<!-- Single root: class goes here -->
<template>
<div>I get the "fancy" class!</div>
</template>
With multiple roots, YOU choose where attributes go:
<template>
<h1>Title</h1>
<p v-bind="$attrs">I get the attributes!</p>
</template>
π Dynamic Components
Dynamic = Changes Based on Conditions
Imagine a TV that can switch channels. The screen stays the same, but the content changes!
Vue uses the special <component> element with :is to swap components:
<script setup>
import TabHome from './TabHome.vue'
import TabPosts from './TabPosts.vue'
import TabArchive from './TabArchive.vue'
import { ref } from 'vue'
const currentTab = ref('TabHome')
const tabs = {
TabHome,
TabPosts,
TabArchive
}
</script>
<template>
<button
v-for="(_, name) in tabs"
@click="currentTab = name"
>
{{ name }}
</button>
<!-- Magic happens here! -->
<component :is="tabs[currentTab]" />
</template>
graph LR A["Click Tab"] --> B{Which Tab?} B -->|Home| C["Show TabHome"] B -->|Posts| D["Show TabPosts"] B -->|Archive| E["Show TabArchive"]
π The is Attribute
The is attribute is like a magic wand that transforms elements!
Use Case 1: Dynamic Components
<component :is="currentComponent" />
Use Case 2: Working with HTML Rules
Some HTML elements have strict rules about what can go inside them:
<!-- β WRONG: <tr> can't be direct child -->
<table>
<my-row></my-row>
</table>
<!-- β
RIGHT: Use is with vue: prefix -->
<table>
<tr is="vue:my-row"></tr>
</table>
This works for:
<table>β needs<tr>,<td><ul>,<ol>β needs<li><select>β needs<option>
The vue: Prefix
When using is in DOM templates, add the vue: prefix:
<tr is="vue:my-component"></tr>
This tells Vue: βHey, this should be a Vue component, not just an HTML attribute!β
π Putting It All Together
Letβs see everything working together in one example:
<script setup>
import { ref, shallowRef } from 'vue'
// Local registration (just import!)
import HomeTab from './HomeTab.vue'
import ProfileTab from './ProfileTab.vue'
import SettingsTab from './SettingsTab.vue'
const tabs = { HomeTab, ProfileTab, SettingsTab }
const activeTab = shallowRef(HomeTab)
</script>
<template>
<!-- Fragment: Multiple root elements -->
<nav>
<button
v-for="(comp, name) in tabs"
:key="name"
@click="activeTab = comp"
>
{{ name.replace('Tab', '') }}
</button>
</nav>
<!-- Dynamic component with is -->
<component :is="activeTab" />
</template>
π Quick Summary
| Concept | What It Does |
|---|---|
| Defining | Create reusable UI pieces with SFC or JS objects |
| Global Registration | Use component anywhere (but avoid overuse) |
| Local Registration | Import where needed (recommended!) |
| Naming | PascalCase in JS, either case in templates |
| Fragments | Multiple root elements (Vue 3 only) |
| Dynamic Components | Swap components with <component :is> |
| is Attribute | Transform elements, bypass HTML restrictions |
π You Did It!
You now understand Vue components like a pro! Remember:
π§© Components are like LEGO blocks - small, reusable, and powerful when combined!
Start small, build up, and soon youβll be creating amazing apps with components!
Happy coding! π
