🎨 Frontend

Vue.js Framework

Last updated: 2025-09-25 02:29:54

Vue.js Progressive Framework

Vue.js is a progressive JavaScript framework for building user interfaces.

Vue 3 Composition API

// Vue 3 component with Composition API




Vue Router

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import User from '../views/User.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // Lazy loading
    component: () => import('../views/About.vue')
  },
  {
    path: '/user/:id',
    name: 'User',
    component: User,
    props: true,
    meta: {
      requiresAuth: true
    }
  },
  {
    path: '/admin',
    children: [
      {
        path: '',
        component: () => import('../views/AdminDashboard.vue')
      },
      {
        path: 'users',
        component: () => import('../views/AdminUsers.vue')
      }
    ],
    beforeEnter: (to, from, next) => {
      // Route-specific guard
      if (store.getters.isAdmin) {
        next()
      } else {
        next('/login')
      }
    }
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

// Global navigation guard
router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !store.getters.isAuthenticated) {
    next('/login')
  } else {
    next()
  }
})

export default router

Vuex State Management

// store/index.js
import { createStore } from 'vuex'
import axios from 'axios'

const store = createStore({
  state: {
    user: null,
    posts: [],
    loading: false,
    error: null
  },
  
  getters: {
    isAuthenticated: state => !!state.user,
    isAdmin: state => state.user?.role === 'admin',
    publishedPosts: state => state.posts.filter(post => post.published)
  },
  
  mutations: {
    SET_LOADING(state, loading) {
      state.loading = loading
    },
    
    SET_ERROR(state, error) {
      state.error = error
    },
    
    SET_USER(state, user) {
      state.user = user
    },
    
    SET_POSTS(state, posts) {
      state.posts = posts
    },
    
    ADD_POST(state, post) {
      state.posts.push(post)
    },
    
    UPDATE_POST(state, updatedPost) {
      const index = state.posts.findIndex(p => p.id === updatedPost.id)
      if (index !== -1) {
        state.posts.splice(index, 1, updatedPost)
      }
    },
    
    DELETE_POST(state, postId) {
      state.posts = state.posts.filter(p => p.id !== postId)
    }
  },
  
  actions: {
    async login({ commit }, credentials) {
      try {
        commit('SET_LOADING', true)
        const response = await axios.post('/api/login', credentials)
        const user = response.data.user
        commit('SET_USER', user)
        localStorage.setItem('token', response.data.token)
        return user
      } catch (error) {
        commit('SET_ERROR', error.response?.data?.message || 'Login failed')
        throw error
      } finally {
        commit('SET_LOADING', false)
      }
    },
    
    async fetchPosts({ commit }) {
      try {
        commit('SET_LOADING', true)
        const response = await axios.get('/api/posts')
        commit('SET_POSTS', response.data)
      } catch (error) {
        commit('SET_ERROR', 'Failed to fetch posts')
      } finally {
        commit('SET_LOADING', false)
      }
    },
    
    async createPost({ commit }, postData) {
      try {
        const response = await axios.post('/api/posts', postData)
        commit('ADD_POST', response.data)
        return response.data
      } catch (error) {
        commit('SET_ERROR', 'Failed to create post')
        throw error
      }
    }
  },
  
  modules: {
    // You can split state into modules
    auth: {
      namespaced: true,
      state: {/* auth specific state */},
      mutations: {/* auth specific mutations */},
      actions: {/* auth specific actions */}
    }
  }
})

export default store

Custom Composables

// composables/useApi.js
import { ref, reactive } from 'vue'
import axios from 'axios'

export function useApi() {
  const loading = ref(false)
  const error = ref(null)
  
  const request = async (config) => {
    try {
      loading.value = true
      error.value = null
      const response = await axios(config)
      return response.data
    } catch (err) {
      error.value = err.response?.data?.message || err.message
      throw err
    } finally {
      loading.value = false
    }
  }
  
  const get = (url, config = {}) => request({ ...config, method: 'GET', url })
  const post = (url, data, config = {}) => request({ ...config, method: 'POST', url, data })
  const put = (url, data, config = {}) => request({ ...config, method: 'PUT', url, data })
  const del = (url, config = {}) => request({ ...config, method: 'DELETE', url })
  
  return {
    loading,
    error,
    request,
    get,
    post,
    put,
    delete: del
  }
}

// composables/useLocalStorage.js
import { ref, watch } from 'vue'

export function useLocalStorage(key, defaultValue) {
  const storedValue = localStorage.getItem(key)
  const value = ref(storedValue ? JSON.parse(storedValue) : defaultValue)
  
  watch(
    value,
    (newValue) => {
      localStorage.setItem(key, JSON.stringify(newValue))
    },
    { deep: true }
  )
  
  return value
}

// Using composables in a component