As someone new to Vue.js, I've been on an exciting journey learning the Composition API. In this article, I want to share what I've discovered so far to help fellow beginners who are also interested in mastering this powerful frontend framework. Everything I'm sharing comes from my own learning process and notes.
Why I Chose to Learn Vue's Composition API
When I started exploring Vue.js, I discovered there are two ways to build components: the Options API (the traditional approach) and the Composition API (introduced in Vue 3). As a beginner, I decided to focus on the Composition API because:
- It lets me organize code by logical concerns rather than option types
- It makes reusing logic between components much easier
- It provides better TypeScript support (which I plan to learn later)
- It's the future direction of Vue
Key Concepts I've Mastered So Far
Reactive State Management
The foundation of Vue is its reactivity system. I learned there are two primary ways to create reactive state:
import { ref, reactive } from 'vue'
// Using ref for primitive values
const count = ref(0) // Access/modify with .value
const message = ref('') // Access/modify with .value
// Using reactive for objects
const user = reactive({
name: 'John',
age: 30,
preferences: {
theme: 'dark'
}
}) // Access/modify directly (user.name, user.preferences.theme)
The important difference I discovered is that with ref
, you need to use .value
to access or change the value in JavaScript code, but not in templates where Vue automatically "unwraps" refs. With reactive
, you access properties directly.
Conditional Rendering
Vue makes showing and hiding content based on conditions remarkably straightforward:
v-if / v-else-if / v-else
This completely adds or removes elements from the DOM:
v-if="status === 'active'">Active Status
v-else-if="status === 'inactive'">Inactive Status
v-else>Unknown Status
v-show
This toggled visibility using CSS display property (element remains in DOM):
v-show="isVisible">This content toggles visibility
I found that v-if
is better when the condition changes infrequently since removing/adding DOM elements is more expensive than toggling visibility with v-show
.
Component Lifecycle Hooks
Understanding when things happen in my components was crucial. The Composition API provides these hooks as functions:
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
// Setup happens when component initializes (like "created" in Options API)
console.log('Setup is running first')
onBeforeMount(() => {
// Right before DOM rendering
console.log('About to mount - DOM not ready yet')
})
onMounted(() => {
console.log('Component mounted - DOM is ready')
// Perfect place for:
// - API calls
// - DOM manipulation
// - Setting up timers/intervals
const timer = setInterval(() => {
count.value++
}, 1000)
// Clean up when component is destroyed
onBeforeUnmount(() => {
clearInterval(timer)
})
})
onBeforeUpdate(() => {
// Called when data changes, before DOM updates
console.log('Data changed, DOM update pending')
})
onUpdated(() => {
// After the DOM has updated
console.log('DOM has been re-rendered')
})
onUnmounted(() => {
// Component is completely destroyed
console.log('Component is gone from DOM')
})
Learning about these hooks helped me understand the perfect timing for different operations in my components.
Working with Computed Properties and Watchers
As I progressed, I realized the power of derived state and reactions:
Computed Properties
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
// Automatically updates when dependencies change
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
// Usage:
console.log(fullName.value) // "John Doe"
// If we update firstName...
firstName.value = 'Jane'
// ...fullName automatically becomes "Jane Doe"
Watchers
import { ref, watch, watchEffect } from 'vue'
const searchQuery = ref('')
const results = ref([])
// Watch specific value with callback
watch(searchQuery, async (newValue, oldValue) => {
if (newValue.trim()) {
results.value = await fetchSearchResults(newValue)
} else {
results.value = []
}
})
// watchEffect runs immediately and tracks dependencies automatically
watchEffect(() => {
console.log(`Current query: ${searchQuery.value}`)
// This runs immediately and again whenever searchQuery changes
})
Data Fetching Patterns
A common task I needed to learn was fetching data from an API:
import { ref, onMounted } from 'vue'
const posts = ref([])
const loading = ref(false)
const error = ref(null)
onMounted(async () => {
loading.value = true
try {
const response = await fetch('https://api.example.com/posts')
posts.value = await response.json()
} catch (e) {
error.value = e
} finally {
loading.value = false
}
})
I discovered that this pattern could be extracted into a reusable function:
// useFetch.js
import { ref, isRef, unref, watchEffect } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(false)
function fetchData() {
data.value = null
error.value = null
loading.value = true
fetch(unref(url))
.then(res => res.json())
.then(json => {
data.value = json
loading.value = false
})
.catch(err => {
error.value = err
loading.value = false
})
}
if (isRef(url)) {
// Re-fetch when URL changes if it's reactive
watchEffect(fetchData)
} else {
// Otherwise, just fetch once
fetchData()
}
return { data, error, loading }
}
// Usage in component:
const apiUrl = ref('https://api.example.com/posts')
const { data: posts, loading, error } = useFetch(apiUrl)
This pattern of creating "composables" (reusable functions with reactive state) is one of my favorite aspects of the Composition API.
Form Handling
Working with forms is a common task in web development. Here's how I learned to implement a basic form with validation:
import { ref } from 'vue'
const formData = ref({
name: '',
email: '',
message: ''
})
const errors = ref({})
const submitted = ref(false)
const validateForm = () => {
errors.value = {}
if (!formData.value.name) {
errors.value.name = 'Name is required'
}
if (!formData.value.email) {
errors.value.email = 'Email is required'
} else if (!/^\S+@\S+\.\S+$/.test(formData.value.email)) {
errors.value.email = 'Email is invalid'
}
return Object.keys(errors.value).length === 0
}
const submitForm = async () => {
if (validateForm()) {
// Submit form data...
submitted.value = true
}
}
In the template:
@submit.prevent="submitForm">
for="name">Name
id="name" v-model="formData.name">
v-if="errors.name">{{ errors.name }}
for="email">Email
id="email" type="email" v-model="formData.email">
v-if="errors.email">{{ errors.email }}
for="message">Message
id="message" v-model="formData.message">
type="submit">Submit
My Learning Experience
As a beginner, I found certain aspects of Vue challenging at first:
- Remembering when to use
.value
with refs (in JS code, but not in templates) - Understanding the difference between
reactive
andref
- Determining when to create a composable vs. just using local functions
What helped me most was building small practice components before attempting more complex features. I started with a simple counter, then added more reactivity, and gradually incorporated lifecycle hooks and data fetching.
Common Vue Directives I Use Daily
Here's a quick reference to the Vue directives I use most often:
Directive | Purpose | Example |
---|---|---|
v-model |
Two-way binding for forms |
|
v-on or @
|
Event handling |
|
v-bind or :
|
Bind attributes |
|
v-if/v-else |
Conditional rendering |
|
v-for |
List rendering |
|
v-show |
Toggle visibility |
|
What's Next on My Learning Path
Now that I've grasped the fundamentals, I'm excited to learn more about:
- State management with Pinia (Vue's recommended state management solution)
- Vue Router for building multi-page applications
- Advanced component patterns and communication
- Unit testing Vue components
- Performance optimization techniques
Resources for Fellow Beginners
If you're also learning Vue.js, I've documented my journey with more detailed examples in my GitHub repository: https://github.com/mohosin2126/vuejs-learning.git. Feel free to check it out for more comprehensive examples and notes.
Conclusion
Learning Vue.js and its Composition API has been an exciting journey so far. As a beginner, I appreciate how Vue balances power and simplicity. The Composition API might seem a bit more complex at first compared to the Options API, but the flexibility and reusability it offers are well worth the initial learning curve.
Are you also learning Vue.js? What parts have you found most challenging or interesting? I'd love to hear about your experience in the comments!
Happy coding!