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