Vue.js: Getting Started
Vue.js is a progressive JavaScript framework for building user interfaces. This article covers Vue.js fundamentals.
Introduction
Vue.js provides:
- Reactive data binding
- Component-based architecture
- Simple template syntax
- Progressive enhancement
- Excellent developer experience
Understanding Vue.js helps you:
- Build interactive UIs
- Create reusable components
- Manage application state
- Handle user interactions
- Build single-page applications
Vue.js Setup
Installation and Project Setup
# โ
Good: Create Vue project with Vite
npm create vite@latest my-app -- --template vue
cd my-app
npm install
npm run dev
# โ
Good: Create Vue project with Vue CLI
npm install -g @vue/cli
vue create my-app
cd my-app
npm run serve
# โ
Good: Add Vue to existing project
npm install vue
Basic Vue Application
// main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
// App.vue
<template>
<div id="app">
<h1>{{ message }}</h1>
<button @click="count++">Count: {{ count }}</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!',
count: 0
}
}
}
</script>
<style scoped>
h1 {
color: #42b983;
}
</style>
Vue Templates
Template Syntax
<!-- โ
Good: Text interpolation -->
<template>
<div>
<p>{{ message }}</p>
<p>{{ count + 1 }}</p>
<p>{{ ok ? 'YES' : 'NO' }}</p>
</div>
</template>
<!-- โ
Good: Attribute binding -->
<template>
<div>
<img :src="imageSrc" :alt="imageAlt" />
<a :href="url">Link</a>
<button :disabled="isDisabled">Click</button>
</div>
</template>
<!-- โ
Good: Event handling -->
<template>
<div>
<button @click="handleClick">Click me</button>
<input @input="handleInput" />
<form @submit.prevent="handleSubmit">
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello',
count: 0,
imageSrc: '/image.jpg',
imageAlt: 'Image',
url: 'https://example.com',
isDisabled: false
}
},
methods: {
handleClick() {
this.count++
},
handleInput(event) {
this.message = event.target.value
},
handleSubmit() {
console.log('Form submitted')
}
}
}
</script>
<!-- โ
Good: Conditional rendering -->
<template>
<div>
<p v-if="seen">Now you see me</p>
<p v-else>Now you don't</p>
<p v-show="visible">Visible</p>
</div>
</template>
<!-- โ
Good: List rendering -->
<template>
<div>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
]
}
}
}
</script>
Vue Components
Single File Components
<!-- UserCard.vue -->
<template>
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>Email: {{ user.email }}</p>
<button @click="$emit('edit', user.id)">Edit</button>
</div>
</template>
<script>
export default {
props: {
user: {
type: Object,
required: true
}
},
emits: ['edit']
}
</script>
<style scoped>
.user-card {
border: 1px solid #ccc;
padding: 1rem;
border-radius: 4px;
}
</style>
<!-- App.vue -->
<template>
<div>
<UserCard
v-for="user in users"
:key="user.id"
:user="user"
@edit="handleEdit"
/>
</div>
</template>
<script>
import UserCard from './components/UserCard.vue'
export default {
components: {
UserCard
},
data() {
return {
users: [
{ id: 1, name: 'John', email: '[email protected]' },
{ id: 2, name: 'Jane', email: '[email protected]' }
]
}
},
methods: {
handleEdit(userId) {
console.log('Edit user:', userId)
}
}
}
</script>
Props and Emits
<!-- Button.vue -->
<template>
<button
:class="['btn', `btn-${variant}`]"
:disabled="disabled"
@click="$emit('click')"
>
<slot></slot>
</button>
</template>
<script>
export default {
props: {
variant: {
type: String,
default: 'primary',
validator: (value) => ['primary', 'secondary', 'danger'].includes(value)
},
disabled: {
type: Boolean,
default: false
}
},
emits: ['click']
}
</script>
<!-- Form.vue -->
<template>
<form @submit.prevent="handleSubmit">
<input
v-model="formData.email"
type="email"
placeholder="Email"
/>
<input
v-model="formData.password"
type="password"
placeholder="Password"
/>
<Button variant="primary" @click="handleSubmit">
Login
</Button>
</form>
</template>
<script>
import Button from './Button.vue'
export default {
components: { Button },
data() {
return {
formData: {
email: '',
password: ''
}
}
},
methods: {
handleSubmit() {
this.$emit('submit', this.formData)
}
}
}
</script>
Vue Reactivity
Reactive Data
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="count++">Increment</button>
<button @click="resetCount">Reset</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
resetCount() {
this.count = 0
}
}
}
</script>
<!-- โ
Good: Computed properties -->
<template>
<div>
<p>Full name: {{ fullName }}</p>
<input v-model="firstName" placeholder="First name" />
<input v-model="lastName" placeholder="Last name" />
</div>
</template>
<script>
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed: {
fullName() {
return `${this.firstName} ${this.lastName}`
}
}
}
</script>
<!-- โ
Good: Watchers -->
<template>
<div>
<input v-model="query" placeholder="Search..." />
<p>Results: {{ results.length }}</p>
</div>
</template>
<script>
export default {
data() {
return {
query: '',
results: []
}
},
watch: {
query(newValue) {
if (newValue) {
this.searchUsers(newValue)
} else {
this.results = []
}
}
},
methods: {
async searchUsers(query) {
const response = await fetch(`/api/search?q=${query}`)
this.results = await response.json()
}
}
}
</script>
Vue Composition API
Setup Function
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { ref, computed, watch } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => {
count.value++
}
const doubleCount = computed(() => count.value * 2)
watch(count, (newValue) => {
console.log('Count changed to:', newValue)
})
return {
count,
increment,
doubleCount
}
}
}
</script>
<!-- โ
Good: Composition API with lifecycle -->
<template>
<div>
<p>User: {{ user.name }}</p>
</div>
</template>
<script>
import { ref, onMounted, onUnmounted } from 'vue'
export default {
setup() {
const user = ref(null)
onMounted(async () => {
const response = await fetch('/api/user')
user.value = await response.json()
})
onUnmounted(() => {
console.log('Component unmounted')
})
return { user }
}
}
</script>
<!-- โ
Good: Custom composables -->
<script>
// useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
const reset = () => {
count.value = initialValue
}
const doubleCount = computed(() => count.value * 2)
return {
count,
increment,
decrement,
reset,
doubleCount
}
}
// Component.vue
import { useCounter } from './useCounter'
export default {
setup() {
const { count, increment, decrement } = useCounter(0)
return {
count,
increment,
decrement
}
}
}
</script>
Vue Router
Basic Routing
// router.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from './pages/Home.vue'
import About from './pages/About.vue'
import UserProfile from './pages/UserProfile.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/users/:id', component: UserProfile }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
// App.vue
<template>
<div>
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<router-view></router-view>
</div>
</template>
Best Practices
-
Use v-for with keys:
<!-- โ Good: Always use key --> <li v-for="item in items" :key="item.id"> {{ item.name }} </li> <!-- โ Bad: No key --> <li v-for="item in items"> {{ item.name }} </li> -
Use computed properties:
<!-- โ Good: Computed property --> <p>{{ fullName }}</p> <!-- โ Bad: Method in template --> <p>{{ getFullName() }}</p> -
Use event modifiers:
<!-- โ Good: Event modifiers --> <form @submit.prevent="handleSubmit"> <button @click.stop="handleClick">Click</button> </form> <!-- โ Bad: Manual prevention --> <form @submit="handleSubmit"> <button @click="handleClick">Click</button> </form>
Summary
Vue.js is powerful. Key takeaways:
- Use Vue templates for reactive UIs
- Create reusable components
- Use computed properties for derived state
- Use watchers for side effects
- Use Composition API for complex logic
- Implement routing with Vue Router
- Follow Vue best practices
- Build scalable applications
Related Resources
Next Steps
- Learn about Angular
- Explore Backend Development
- Study Express.js
- Practice Vue.js
- Build Vue.js applications
Comments