🏠 Vue.js Single File Components: Building Your Dream House
Imagine you’re building a LEGO house. You have three types of blocks:
- The shape blocks (what it looks like)
- The brain blocks (how it thinks)
- The paint blocks (the colors and style)
In Vue.js, a Single File Component (SFC) is exactly like that LEGO house. Everything you need is in ONE file with the .vue extension!
🎯 What is a Single File Component?
Think of an SFC like a recipe card. One card has:
- A picture of the dish (Template)
- The cooking steps (Script)
- How to plate it prettily (Style)
<!-- MyComponent.vue -->
<template>
<!-- What users SEE -->
</template>
<script setup>
// How it THINKS
</script>
<style scoped>
/* How it LOOKS */
</style>
Why is this amazing?
- Everything in ONE place
- Easy to find things
- Like having your whole recipe on one card!
📝 The Template Section: The Face of Your Component
The <template> is what people SEE. It’s like the front of a greeting card.
<template>
<div class="greeting-card">
<h1>{{ message }}</h1>
<button @click="sayHello">
Click Me!
</button>
</div>
</template>
Key Rules:
- Must have ONE root element (the wrapper
<div>) - Uses
{{ }}to show data (like a window to your data) - Uses
@clickto listen for clicks
graph TD A["Template Section"] --> B["HTML Structure"] B --> C["Shows Data with curly braces"] B --> D["Listens for Events"] B --> E["One Root Element"]
🧠 The Script Section: The Brain
The <script> is where your component THINKS. It holds:
- Data (what the component remembers)
- Methods (what the component can do)
- Logic (how the component decides)
Old Way (Options API):
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
Think of this like filling out a form with specific boxes for each thing.
🎨 The Style Section: The Fashion Designer
The <style> section makes things PRETTY. It’s the makeup and clothes for your component.
<style scoped>
.greeting-card {
background: #42b883;
padding: 20px;
border-radius: 10px;
}
button {
background: white;
color: #42b883;
}
</style>
The Magic Word: scoped
- Without scoped: Styles apply to EVERYTHING (messy!)
- With scoped: Styles only apply to THIS component (clean!)
It’s like wearing a name tag. Only YOUR clothes stay with YOU.
⚡ Script Setup Syntax: The Shortcut
Remember the “old way” with export default? There’s a faster, cleaner way!
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
What Changed?
| Old Way | Script Setup |
|---|---|
export default |
Not needed! |
data() |
Use ref() |
this.count |
Just count.value |
| More typing | Less typing! |
Script Setup automatically makes everything available in your template. No more “exporting” things!
🪄 Script Setup Macros: Magic Words
Macros are special helper words that only work inside <script setup>. They’re like magic spells!
defineProps - Receiving Gifts
When a parent gives your component data:
<script setup>
const props = defineProps({
name: String,
age: Number
})
</script>
<template>
<p>Hello, {{ name }}! You are {{ age }}.</p>
</template>
defineEmits - Sending Messages
When your component wants to tell the parent something:
<script setup>
const emit = defineEmits(['save', 'cancel'])
function handleSave() {
emit('save', { data: 'my data' })
}
</script>
defineExpose - Sharing Secrets
Let parents access your component’s inner things:
<script setup>
import { ref } from 'vue'
const secretNumber = ref(42)
defineExpose({
secretNumber
})
</script>
graph TD A["Script Setup Macros"] --> B["defineProps"] A --> C["defineEmits"] A --> D["defineExpose"] B --> E["Receive data from parent"] C --> F["Send events to parent"] D --> G["Share inner data with parent"]
🛠️ Composition API Helpers: Your Toolbox
The Composition API gives you powerful tools to organize your code.
ref - A Box for One Value
<script setup>
import { ref } from 'vue'
const message = ref('Hello!')
// To read: message.value
// To change: message.value = 'Bye!'
</script>
reactive - A Box for Many Values
<script setup>
import { reactive } from 'vue'
const user = reactive({
name: 'Alex',
age: 10,
hobby: 'coding'
})
// To read: user.name
// To change: user.age = 11
</script>
computed - Auto-Calculating Values
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => {
return firstName.value + ' ' + lastName.value
})
// fullName updates automatically!
</script>
watch - The Spy
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`Changed from ${oldVal} to ${newVal}`)
})
</script>
| Helper | Purpose | Think of it as… |
|---|---|---|
ref |
Single value | A labeled box |
reactive |
Object with values | A labeled drawer |
computed |
Auto-calculated | A calculator |
watch |
React to changes | A spy watching |
⚔️ Composition vs Options API: The Big Choice
Options API (The Form Approach)
<script>
export default {
data() {
return { count: 0 }
},
computed: {
doubled() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
Good for: Beginners, simple components
Composition API (The Toolbox Approach)
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
</script>
Good for: Complex logic, reusable code
graph LR A["Choose Your Style"] --> B["Options API"] A --> C["Composition API"] B --> D["Organized by type"] B --> E["Uses &#39;this&#39; keyword"] B --> F["Great for beginners"] C --> G["Organized by feature"] C --> H["No &#39;this&#39; needed"] C --> I["Better for complex apps"]
The Big Difference
| Feature | Options API | Composition API |
|---|---|---|
| Organization | By type | By feature |
| Learning curve | Easier | Slightly harder |
| Code reuse | Mixins | Composables |
| TypeScript | OK | Great! |
this keyword |
Required | Not needed |
🎉 Putting It All Together
Here’s a complete SFC using everything we learned:
<template>
<div class="counter-app">
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
<p>Doubled: {{ doubled }}</p>
<button @click="increment">
Add One
</button>
<button @click="reset">
Reset
</button>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
// Props from parent
const props = defineProps({
title: {
type: String,
default: 'My Counter'
}
})
// Emit events to parent
const emit = defineEmits(['updated'])
// Reactive data
const count = ref(0)
// Computed property
const doubled = computed(() => count.value * 2)
// Methods
function increment() {
count.value++
emit('updated', count.value)
}
function reset() {
count.value = 0
emit('updated', count.value)
}
</script>
<style scoped>
.counter-app {
background: linear-gradient(135deg, #42b883, #35495e);
padding: 20px;
border-radius: 12px;
color: white;
text-align: center;
}
button {
margin: 5px;
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
}
</style>
🌟 Summary: Your SFC Checklist
✅ Template = What users see (HTML)
✅ Script = How it thinks (JavaScript)
✅ Style = How it looks (CSS)
✅ Script Setup = The modern, clean way to write logic
✅ Macros = defineProps, defineEmits, defineExpose
✅ Helpers = ref, reactive, computed, watch
✅ Two Styles = Options API (form) vs Composition API (toolbox)
You did it! 🎊 You now understand Vue.js Single File Components. Each .vue file is like a complete LEGO set - everything you need in one place, ready to build amazing things!
