Vue.js Framework
Introduction to Vue.js
Vue.js is a progressive JavaScript framework for building user interfaces. It features a powerful reactivity system and component-based architecture.
Key Features:
- Progressive framework
- Virtual DOM
- Reactive data binding
- Component system
- Built-in state management
- Rich ecosystem
Project Setup
# Create new project with Vite
npm create vite@latest my-vue-app -- --template vue
# Install dependencies
cd my-vue-app
npm install
# Add additional tools
npm install vue-router@4
npm install pinia
npm install @vueuse/core
npm install axios
# Development server
npm run dev
Composition API
Basic Component
<script setup>
import { ref, computed, onMounted } from 'vue'
// Reactive state
const count = ref(0)
const message = ref('Hello Vue!')
// Computed property
const doubleCount = computed(() => count.value * 2)
// Methods
const increment = () => {
count.value++
}
// Lifecycle hooks
onMounted(() => {
console.log('Component mounted')
})
</script>
<template>
<div class="container">
<h1>{{ message }}</h1>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<style scoped>
.container {
padding: 20px;
}
button {
margin-top: 10px;
}
</style>
Composables
// useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
function decrement() {
count.value--
}
return {
count,
doubleCount,
increment,
decrement
}
}
// useApi.js
import { ref } from 'vue'
import axios from 'axios'
export function useApi(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(false)
async function fetch() {
loading.value = true
try {
const response = await axios.get(url)
data.value = response.data
} catch (e) {
error.value = e
} finally {
loading.value = false
}
}
return {
data,
error,
loading,
fetch
}
}
Options API
Component Example
<template>
<div class="user-profile">
<h2>{{ fullName }}</h2>
<p>Email: {{ user.email }}</p>
<button @click="updateProfile">Update</button>
</div>
</template>
<script>
export default {
name: 'UserProfile',
props: {
userId: {
type: String,
required: true
}
},
data() {
return {
user: {
firstName: '',
lastName: '',
email: ''
}
}
},
computed: {
fullName() {
return `${this.user.firstName} ${this.user.lastName}`
}
},
methods: {
async fetchUser() {
try {
const response = await this.axios.get(
`/api/users/${this.userId}`
)
this.user = response.data
} catch (error) {
console.error('Failed to fetch user:', error)
}
},
async updateProfile() {
try {
await this.axios.put(
`/api/users/${this.userId}`,
this.user
)
this.$emit('profile-updated', this.user)
} catch (error) {
console.error('Failed to update profile:', error)
}
}
},
created() {
this.fetchUser()
},
watch: {
userId: {
handler: 'fetchUser',
immediate: true
}
}
}
</script>
Vue Router
Router Configuration
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue')
},
{
path: '/users',
name: 'Users',
component: () => import('../views/Users.vue'),
children: [
{
path: ':id',
name: 'UserProfile',
component: () => import('../views/UserProfile.vue'),
props: true
}
]
},
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue'),
meta: { requiresGuest: true }
},
{
path: '/dashboard',
name: 'Dashboard',
component: () => import('../views/Dashboard.vue'),
meta: { requiresAuth: true }
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// Navigation guards
router.beforeEach((to, from, next) => {
const isAuthenticated = localStorage.getItem('token')
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login')
} else if (to.meta.requiresGuest && isAuthenticated) {
next('/dashboard')
} else {
next()
}
})
export default router
State Management
Pinia Store
// stores/user.js
import { defineStore } from 'pinia'
import axios from 'axios'
export const useUserStore = defineStore('user', {
state: () => ({
user: null,
loading: false,
error: null
}),
getters: {
isAuthenticated: (state) => !!state.user,
username: (state) => state.user?.username
},
actions: {
async login(credentials) {
this.loading = true
try {
const response = await axios.post(
'/api/login',
credentials
)
this.user = response.data.user
localStorage.setItem('token', response.data.token)
} catch (error) {
this.error = error.message
throw error
} finally {
this.loading = false
}
},
async logout() {
try {
await axios.post('/api/logout')
this.user = null
localStorage.removeItem('token')
} catch (error) {
console.error('Logout failed:', error)
}
},
async fetchUser() {
if (!localStorage.getItem('token')) return
try {
const response = await axios.get('/api/user')
this.user = response.data
} catch (error) {
this.logout()
}
}
}
})
Best Practices
Component Design:
- Single Responsibility Principle
- Props validation
- Events for parent communication
- Composables for code reuse
- Proper computed vs methods usage
- Scoped styles
Performance:
- Component lazy loading
- Virtual scrolling for large lists
- Computed property caching
- Keep-alive for expensive components
- Debounce/throttle event handlers
- Tree-shaking
Testing:
- Unit testing with Vitest
- Component testing with Testing Library
- E2E testing with Cypress
- Mock API calls
- Test store actions and mutations
- Snapshot testing