Domande essenziali su Vue.js: 25 domande per ottenere il lavoro

Preparati ai colloqui Vue.js con queste 25 domande essenziali. Dalla reattività ai composable, padroneggia i concetti chiave per il prossimo colloquio.

Illustrazione di un colloquio tecnico Vue.js con blocchi di codice e logo Vue

I colloqui su Vue.js valutano molto più della sintassi del framework. I recruiter vogliono capire la padronanza del sistema di reattività, l'organizzazione del codice con la Composition API e la capacità di affrontare problemi reali di performance e di architettura.

Consiglio di preparazione

Ogni domanda include una risposta dettagliata ed esempi di codice. Per i colloqui tecnici conviene esercitarsi a spiegare i concetti ad alta voce, come durante un vero colloquio.

Domande fondamentali su Vue.js

1. Qual è la differenza tra ref e reactive in Vue 3?

Questa domanda valuta la comprensione del sistema di reattività, elemento fondamentale di Vue 3. La differenza principale risiede nei tipi di dati gestiti e nella sintassi di accesso.

ref crea un riferimento reattivo per valori primitivi (string, number, boolean) e richiede .value per accedere al valore nello script. reactive crea un proxy reattivo per gli oggetti e consente l'accesso diretto alle proprietà.

javascript
// Esempio comparativo ref vs reactive
import { ref, reactive } from 'vue'

// ref: for primitives
// Requires .value in the script
const count = ref(0)
count.value++ // Access with .value

// reactive: for complex objects
// Direct property access
const user = reactive({
  name: 'Alice',
  age: 25
})
user.age++ // No .value needed

// Warning: reactive loses reactivity if reassigned
// user = { name: 'Bob', age: 30 } // ❌ Breaks reactivity
Object.assign(user, { name: 'Bob', age: 30 }) // ✅ Correct

Regola generale: usare ref per valori semplici e reactive per oggetti strutturati con più proprietà correlate.

2. Come funziona il sistema di reattività di Vue 3?

Vue 3 usa i Proxy di JavaScript (ES6) per intercettare le operazioni sugli oggetti reattivi. A differenza di Vue 2, che usava Object.defineProperty, questo approccio rileva dinamicamente l'aggiunta e la rimozione di proprietà.

javascript
// Simplified demonstration of the reactivity principle
// Vue uses Proxies to track dependencies

const handler = {
  // Intercept reading
  get(target, key, receiver) {
    track(target, key) // Register the dependency
    return Reflect.get(target, key, receiver)
  },
  // Intercept writing
  set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver)
    trigger(target, key) // Trigger updates
    return result
  }
}

// Creating a reactive proxy
const reactiveObject = new Proxy(originalObject, handler)

Tra i vantaggi del Proxy: rilevamento delle nuove proprietà, supporto a Map e Set e migliore performance su oggetti di grandi dimensioni.

3. Spiega la differenza tra computed e watch

computed e watch rispondono a esigenze diverse nella gestione della reattività.

Computed: calcola un valore derivato a partire da altri dati reattivi. I valori vengono memorizzati nella cache e ricalcolati solo quando le dipendenze cambiano. Ideale per le trasformazioni di dati.

Watch: esegue effetti collaterali in risposta ai cambiamenti. Utile per chiamate ad API, interazioni con il DOM o operazioni asincrone.

javascript
// Computed vs watch comparison
import { ref, computed, watch } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

// computed: derived value with cache
// Recalculates only if firstName or lastName changes
const fullName = computed(() => {
  console.log('Computing full name') // Called only once
  return `${firstName.value} ${lastName.value}`
})

// Multiple accesses = single execution (cached)
console.log(fullName.value) // "John Doe"
console.log(fullName.value) // No recalculation

// watch: side effect without cache
// Executed on every change
watch(firstName, async (newName, oldName) => {
  // Side effect: API call
  await saveToServer({ firstName: newName })
  console.log(`Name changed from ${oldName} to ${newName}`)
})

4. Cos'è il Virtual DOM e come lo utilizza Vue?

Il Virtual DOM è una rappresentazione leggera del DOM reale in JavaScript. Vue mantiene un albero virtuale in memoria e calcola le differenze (diffing) tra lo stato precedente e quello nuovo per applicare al DOM reale solo i cambiamenti necessari.

javascript
// Conceptual representation of the Virtual DOM
// Vue creates this structure internally

const vnode = {
  type: 'div',
  props: {
    class: 'container',
    id: 'app'
  },
  children: [
    {
      type: 'h1',
      props: {},
      children: 'Title'
    },
    {
      type: 'p',
      props: {},
      children: 'Paragraph content'
    }
  ]
}

// When changes occur, Vue compares vnodes
// and updates only the modified elements

Le ottimizzazioni di Vue 3 includono: hoisting dei nodi statici, patch flag per identificare il tipo di cambiamento e tree-shaking nel compilatore.

5. Come gestire la comunicazione tra componenti senza relazione diretta?

Esistono diversi pattern per far comunicare componenti che non condividono una relazione padre-figlio diretta.

javascript
// Solution 1: Event Bus (small applications)
// eventBus.js
import { ref } from 'vue'

const bus = ref(new Map())

export function useEventBus() {
  // Emit an event
  const emit = (event, payload) => {
    const callbacks = bus.value.get(event) || []
    callbacks.forEach(cb => cb(payload))
  }

  // Listen to an event
  const on = (event, callback) => {
    if (!bus.value.has(event)) {
      bus.value.set(event, [])
    }
    bus.value.get(event).push(callback)
  }

  return { emit, on }
}
javascript
// Solution 2: Provide/Inject for nested components
// Ancestor
import { provide, ref } from 'vue'

const sharedState = ref('shared value')
provide('stateKey', sharedState)

// Descendant (any level)
import { inject } from 'vue'

const state = inject('stateKey')

Per applicazioni più complesse, Pinia resta la soluzione consigliata per la gestione dello stato globale.

Domande sulla Composition API

6. Quali vantaggi offre la Composition API rispetto alla Options API?

La Composition API offre diversi vantaggi strutturali rispetto alla Options API tradizionale.

Organizzazione per funzionalità: il codice relativo alla stessa feature è raggruppato, mentre la Options API separa per tipo (data, methods, computed).

Riuso tramite composables: estrazione semplice della logica in funzioni riutilizzabili.

Migliore supporto a TypeScript: inferenza dei tipi naturale, senza decoratori.

javascript
// Options API: code fragmented by type
export default {
  data() {
    return {
      searchQuery: '',
      results: []
    }
  },
  computed: {
    hasResults() {
      return this.results.length > 0
    }
  },
  methods: {
    async search() {
      this.results = await fetchResults(this.searchQuery)
    }
  },
  watch: {
    searchQuery: 'search'
  }
}

// Composition API: code grouped by feature
import { ref, computed, watch } from 'vue'

export function useSearch() {
  const searchQuery = ref('')
  const results = ref([])

  const hasResults = computed(() => results.value.length > 0)

  const search = async () => {
    results.value = await fetchResults(searchQuery.value)
  }

  watch(searchQuery, search)

  return { searchQuery, results, hasResults, search }
}

7. Come si crea un composable riutilizzabile?

I composable sono funzioni che incapsulano logica reattiva. Le convenzioni includono: prefisso use, ritorno di un oggetto con stato e metodi, e gestione del cleanup.

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

// Composable to synchronize state with localStorage
export function useLocalStorage(key, defaultValue) {
  // Retrieve initial value from localStorage
  const storedValue = localStorage.getItem(key)
  const data = ref(
    storedValue ? JSON.parse(storedValue) : defaultValue
  )

  // Synchronize changes to localStorage
  watch(
    data,
    (newValue) => {
      if (newValue === null) {
        localStorage.removeItem(key)
      } else {
        localStorage.setItem(key, JSON.stringify(newValue))
      }
    },
    { deep: true } // Observe nested objects
  )

  return data
}

// Usage in a component
const theme = useLocalStorage('theme', 'light')
const userPrefs = useLocalStorage('prefs', { notifications: true })
Convenzione di denominazione

I composable seguono la convenzione useXxx per indicare la loro natura riutilizzabile. Questa convenzione migliora la leggibilità e facilita l'identificazione delle dipendenze reattive.

8. Spiega watchEffect rispetto a watch

watchEffect e watch reagiscono entrambi ai cambiamenti, ma con approcci differenti.

watchEffect: viene eseguito subito e ripetuto automaticamente quando cambiano le sue dipendenze reattive. Il tracciamento delle dipendenze è automatico.

watch: osserva sorgenti specifiche e fornisce valori vecchi e nuovi. Permette un maggiore controllo su quando si attiva.

javascript
// watchEffect vs watch comparison
import { ref, watch, watchEffect } from 'vue'

const userId = ref(1)
const userData = ref(null)

// watchEffect: automatic tracking
// Runs immediately
watchEffect(async () => {
  // userId is automatically tracked
  const response = await fetch(`/api/users/${userId.value}`)
  userData.value = await response.json()
})

// watch: explicit sources with old values
watch(userId, async (newId, oldId) => {
  console.log(`User changed from ${oldId} to ${newId}`)
  const response = await fetch(`/api/users/${newId}`)
  userData.value = await response.json()
}, {
  immediate: true // Run immediately like watchEffect
})

// watchEffect with cleanup
watchEffect((onCleanup) => {
  const controller = new AbortController()

  fetch(`/api/users/${userId.value}`, {
    signal: controller.signal
  }).then(/* ... */)

  // Cleanup: cancel previous request
  onCleanup(() => controller.abort())
})

9. Come si gestiscono le props con TypeScript in script setup?

La sintassi <script setup> offre integrazione nativa con TypeScript tramite defineProps e withDefaults.

TypedComponent.vuetypescript
<script setup lang="ts">
// Interface for props
interface Props {
  title: string
  count?: number
  items?: string[]
  onSubmit?: (data: FormData) => void
}

// defineProps with generic typing
// withDefaults for default values
const props = withDefaults(defineProps<Props>(), {
  count: 0,
  items: () => [], // Factory for objects/arrays
  onSubmit: undefined
})

// Props are automatically typed
console.log(props.title) // string
console.log(props.count) // number

// defineEmits with typing
const emit = defineEmits<{
  (e: 'update', value: number): void
  (e: 'delete', id: string): void
}>()

// Typed emit usage
const handleUpdate = () => {
  emit('update', props.count + 1)
}
</script>

Pronto a superare i tuoi colloqui su Vue.js / Nuxt.js?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Domande sulla performance

10. Quali tecniche di ottimizzazione delle performance esistono?

Vue 3 offre diversi meccanismi per ottimizzare le performance.

1. v-once: single render for static contentjavascript
<template>
  <div v-once>
    <!-- This content will never be re-rendered -->
    <ComplexStaticComponent />
  </div>
</template>

// 2. v-memo: conditional memoization
<template>
  <div v-for="item in list" :key="item.id" v-memo="[item.id, item.selected]">
    <!-- Re-renders only if id or selected changes -->
    {{ item.name }}
  </div>
</template>

// 3. shallowRef/shallowReactive: shallow reactivity
import { shallowRef, triggerRef } from 'vue'

// Only tracks ref replacement, not internal mutations
const largeList = shallowRef([/* thousands of elements */])

// Force update after mutation
largeList.value.push(newItem)
triggerRef(largeList) // Manually trigger re-render
4. Async components for code-splittingjavascript
import { defineAsyncComponent } from 'vue'

const HeavyComponent = defineAsyncComponent({
  loader: () => import('./HeavyComponent.vue'),
  loadingComponent: LoadingSpinner,
  delay: 200, // Delay before showing loading
  errorComponent: ErrorDisplay,
  timeout: 3000
})

// 5. KeepAlive for component caching
<template>
  <KeepAlive :include="['Dashboard', 'UserProfile']" :max="10">
    <component :is="currentView" />
  </KeepAlive>
</template>

11. Come evitare i re-render inutili?

I re-render inutili penalizzano le performance. Diverse strategie aiutano a ridurli al minimo.

javascript
// Problem: function created on each render
<template>
  <!--New function on every render -->
  <ChildComponent @click="() => handleClick(item.id)" />
</template>

// Solution: use a method or ref
<script setup>
const handleItemClick = (id) => {
  // Processing logic
}
</script>

<template>
  <!--Stable reference -->
  <ChildComponent @click="handleItemClick(item.id)" />
</template>
javascript
// Using computed for expensive calculations
import { computed } from 'vue'

// ❌ Recalculated on every render
const getFilteredItems = () => {
  return items.value.filter(/* complex logic */)
}

// ✅ Cached, recalculated only if items changes
const filteredItems = computed(() => {
  return items.value.filter(/* complex logic */)
})

12. Spiega il lazy loading dei componenti e delle rotte

Il lazy loading permette di caricare il codice on demand, riducendo la dimensione del bundle iniziale.

router/index.jsjavascript
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      // Immediate loading (main bundle)
      component: () => import('@/views/Home.vue')
    },
    {
      path: '/dashboard',
      // Separate chunk with custom name
      component: () => import(
        /* webpackChunkName: "dashboard" */
        '@/views/Dashboard.vue'
      ),
      // Lazy loading child routes
      children: [
        {
          path: 'analytics',
          component: () => import('@/views/Analytics.vue')
        }
      ]
    },
    {
      path: '/admin',
      // Prefetch on link hover
      component: () => import('@/views/Admin.vue'),
      meta: { prefetch: true }
    }
  ]
})

export default router

Domande su Vue Router

13. Come si proteggono le rotte con i guard?

I navigation guard permettono di controllare l'accesso alle rotte.

router/index.jsjavascript
import { createRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'

const router = createRouter({
  routes: [
    {
      path: '/dashboard',
      component: Dashboard,
      meta: { requiresAuth: true, roles: ['admin', 'user'] }
    }
  ]
})

// Global guard: checks authentication
router.beforeEach(async (to, from, next) => {
  const auth = useAuthStore()

  // Public route
  if (!to.meta.requiresAuth) {
    return next()
  }

  // Check authentication
  if (!auth.isAuthenticated) {
    return next({
      path: '/login',
      query: { redirect: to.fullPath }
    })
  }

  // Check roles if specified
  if (to.meta.roles && !to.meta.roles.includes(auth.user.role)) {
    return next('/unauthorized')
  }

  next()
})

// Component-level guard
export default {
  beforeRouteEnter(to, from, next) {
    // No access to this here
    next(vm => {
      // Access component instance via vm
      vm.loadData()
    })
  },
  beforeRouteLeave(to, from, next) {
    // Confirm before leaving if form modified
    if (this.hasUnsavedChanges) {
      const answer = confirm('Leave without saving?')
      next(answer)
    } else {
      next()
    }
  }
}

14. Come si passano le props alle rotte?

Vue Router consente di disaccoppiare i componenti dai parametri di rotta.

javascript
// Route configuration with props
const routes = [
  {
    path: '/user/:id',
    component: UserProfile,
    // Boolean mode: passes params as props
    props: true
  },
  {
    path: '/search',
    component: SearchResults,
    // Function mode: custom transformation
    props: (route) => ({
      query: route.query.q,
      page: parseInt(route.query.page) || 1,
      filters: route.query.filters?.split(',') || []
    })
  },
  {
    path: '/static',
    component: StaticPage,
    // Object mode: static props
    props: { sidebar: true, theme: 'dark' }
  }
]
UserProfile.vuejavascript
<script setup>
// Props are automatically injected
defineProps<{
  id: string
}>()
</script>

// SearchResults.vue
<script setup>
defineProps<{
  query: string
  page: number
  filters: string[]
}>()
</script>

Domande su Pinia e gestione dello stato

15. Quali sono le differenze tra Pinia e Vuex?

Pinia è il gestore di stato ufficiale di Vue 3 e sostituisce Vuex con un'API semplificata.

| Caratteristica | Vuex | Pinia | |---------|------|-------| | Mutations | Obbligatorie | Non necessarie | | Moduli | Configurazione complessa | Store indipendenti | | TypeScript | Supporto limitato | Nativo e completo | | API | Options | Composition + Options | | DevTools | Supporto | Supporto completo |

javascript
// Pinia Store with Composition API
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCartStore = defineStore('cart', () => {
  // State
  const items = ref([])
  const discountCode = ref(null)

  // Getters (computed)
  const totalItems = computed(() =>
    items.value.reduce((sum, item) => sum + item.quantity, 0)
  )

  const totalPrice = computed(() => {
    const subtotal = items.value.reduce(
      (sum, item) => sum + item.price * item.quantity, 0
    )
    return discountCode.value ? subtotal * 0.9 : subtotal
  })

  // Actions (direct functions)
  function addItem(product) {
    const existing = items.value.find(i => i.id === product.id)
    if (existing) {
      existing.quantity++
    } else {
      items.value.push({ ...product, quantity: 1 })
    }
  }

  function removeItem(productId) {
    const index = items.value.findIndex(i => i.id === productId)
    if (index > -1) {
      items.value.splice(index, 1)
    }
  }

  async function checkout() {
    const response = await api.createOrder(items.value)
    items.value = []
    return response
  }

  return {
    items, discountCode,
    totalItems, totalPrice,
    addItem, removeItem, checkout
  }
})

16. Come si rende persistente lo stato di uno store Pinia?

La persistenza permette di mantenere lo stato tra le sessioni dell'utente.

plugins/piniaPersistedState.jsjavascript
import { watch } from 'vue'

export function createPersistedState(options = {}) {
  const {
    key = 'pinia',
    storage = localStorage,
    paths = null
  } = options

  return ({ store }) => {
    // Restore state on startup
    const savedState = storage.getItem(`${key}-${store.$id}`)
    if (savedState) {
      store.$patch(JSON.parse(savedState))
    }

    // Persist changes
    watch(
      () => store.$state,
      (state) => {
        const toSave = paths
          ? paths.reduce((acc, path) => {
              acc[path] = state[path]
              return acc
            }, {})
          : state

        storage.setItem(
          `${key}-${store.$id}`,
          JSON.stringify(toSave)
        )
      },
      { deep: true }
    )
  }
}

// main.js
import { createPinia } from 'pinia'
import { createPersistedState } from './plugins/piniaPersistedState'

const pinia = createPinia()
pinia.use(createPersistedState({
  key: 'app-state',
  paths: ['user', 'preferences'] // Persist only these keys
}))
Dati sensibili

Conviene evitare la persistenza di dati sensibili (token, password) nel localStorage. Per i token di autenticazione sono più sicuri i cookie httpOnly.

Domande avanzate

17. Come si implementa un sistema di plugin in Vue?

I plugin permettono di estendere Vue con funzionalità globali.

plugins/analyticsPlugin.jsjavascript
export const AnalyticsPlugin = {
  install(app, options = {}) {
    const { trackingId, debug = false } = options

    // Global injection available in all components
    const analytics = {
      trackEvent(category, action, label) {
        if (debug) {
          console.log('Analytics:', { category, action, label })
        }
        // Logic to send to analytics service
        window.gtag?.('event', action, {
          event_category: category,
          event_label: label
        })
      },
      trackPage(path) {
        window.gtag?.('config', trackingId, { page_path: path })
      }
    }

    // Make available via inject
    app.provide('analytics', analytics)

    // Add global property (discouraged in Composition API)
    app.config.globalProperties.$analytics = analytics

    // Custom directive for click tracking
    app.directive('track', {
      mounted(el, binding) {
        el.addEventListener('click', () => {
          analytics.trackEvent('click', binding.value, el.textContent)
        })
      }
    })

    // Automatic route change tracking
    app.mixin({
      mounted() {
        if (this.$route) {
          analytics.trackPage(this.$route.path)
        }
      }
    })
  }
}

// main.js
import { AnalyticsPlugin } from './plugins/analyticsPlugin'

app.use(AnalyticsPlugin, {
  trackingId: 'UA-XXXXX-X',
  debug: import.meta.env.DEV
})

18. Spiega le Render Functions e la loro utilità

Le render function offrono il controllo totale sul rendering, utili per componenti molto dinamici.

components/DynamicHeading.jsjavascript
import { h } from 'vue'

// Functional component with render function
export const DynamicHeading = {
  props: {
    level: {
      type: Number,
      default: 1,
      validator: (v) => v >= 1 && v <= 6
    }
  },
  setup(props, { slots }) {
    // h() creates a vnode
    // Arguments: type, props, children
    return () => h(
      `h${props.level}`,
      { class: 'dynamic-heading' },
      slots.default?.()
    )
  }
}

// Component with complex conditional logic
export const ConditionalWrapper = {
  props: ['condition', 'wrapper'],
  setup(props, { slots }) {
    return () => {
      if (props.condition) {
        return h(props.wrapper, null, slots.default?.())
      }
      return slots.default?.()
    }
  }
}

// Usage
<DynamicHeading :level="2">Level 2 Title</DynamicHeading>

<ConditionalWrapper :condition="isLink" wrapper="a">
  Conditional content
</ConditionalWrapper>

19. Come si testano i componenti Vue con Vitest?

I test unitari validano il comportamento isolato dei componenti.

components/__tests__/Counter.spec.jsjavascript
import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import Counter from '../Counter.vue'

describe('Counter', () => {
  it('displays the initial value', () => {
    const wrapper = mount(Counter, {
      props: { initialValue: 5 }
    })

    expect(wrapper.text()).toContain('5')
  })

  it('increments the value on click', async () => {
    const wrapper = mount(Counter)

    await wrapper.find('button.increment').trigger('click')

    expect(wrapper.text()).toContain('1')
  })

  it('emits an event when changed', async () => {
    const wrapper = mount(Counter)

    await wrapper.find('button.increment').trigger('click')

    expect(wrapper.emitted('change')).toBeTruthy()
    expect(wrapper.emitted('change')[0]).toEqual([1])
  })

  it('calls the service on submit', async () => {
    const mockSubmit = vi.fn()
    const wrapper = mount(Counter, {
      global: {
        provide: {
          submitService: mockSubmit
        }
      }
    })

    await wrapper.find('form').trigger('submit')

    expect(mockSubmit).toHaveBeenCalled()
  })
})

20. Come si gestiscono gli errori in modo globale in Vue?

Vue 3 offre diversi meccanismi per intercettare e gestire gli errori.

main.jsjavascript
import { createApp } from 'vue'

const app = createApp(App)

// Global handler for component errors
app.config.errorHandler = (err, instance, info) => {
  // err: the error
  // instance: the component instance
  // info: string describing where the error occurred

  console.error('Vue error:', err)
  console.error('Component:', instance?.$options?.name)
  console.error('Info:', info)

  // Send to monitoring service
  errorTracker.captureException(err, {
    component: instance?.$options?.name,
    info
  })
}

// Handler for warnings (dev only)
app.config.warnHandler = (msg, instance, trace) => {
  console.warn('Vue warning:', msg)
}
javascript
// ErrorBoundary component
<script setup>
import { onErrorCaptured, ref } from 'vue'

const error = ref(null)

// Captures errors from child components
onErrorCaptured((err, instance, info) => {
  error.value = {
    message: err.message,
    component: instance?.$options?.name,
    info
  }

  // Return false to stop propagation
  return false
})

const retry = () => {
  error.value = null
}
</script>

<template>
  <div v-if="error" class="error-boundary">
    <h2>An error occurred</h2>
    <p>{{ error.message }}</p>
    <button @click="retry">Retry</button>
  </div>
  <slot v-else />
</template>

Pronto a superare i tuoi colloqui su Vue.js / Nuxt.js?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Domande sulle best practice

21. Quali convenzioni di denominazione seguire in Vue?

Le convenzioni di denominazione migliorano la leggibilità e la manutenzione del codice.

javascript
// Component naming
// PascalCase for files and names
// BaseButton.vue, AppHeader.vue, TheNavbar.vue

// Props: camelCase in JS, kebab-case in template
defineProps<{
  userName: string        // JS
  isActive: boolean       // JS
}>()

// <UserCard :user-name="name" :is-active="active" />

// Events: camelCase with action prefix
const emit = defineEmits<{
  (e: 'updateValue', value: string): void  // ✅
  (e: 'submit'): void                       // ✅
  (e: 'value-updated'): void               // ❌ Avoid
}>()

// Composables: use prefix
// useAuth.js, useFetch.js, useLocalStorage.js

// Pinia stores: use prefix + Store suffix
// useUserStore, useCartStore, useSettingsStore

// Constants: SCREAMING_SNAKE_CASE
const MAX_RETRY_COUNT = 3
const API_BASE_URL = '/api/v1'

22. Come si struttura un progetto Vue di larga scala?

Una struttura modulare facilita la navigazione e la manutenzione.

text
src/
├── assets/              # Static files
├── components/
│   ├── ui/              # Generic components (Button, Modal)
│   └── common/          # Reusable business components
├── composables/         # Reusable logic
│   ├── useAuth.js
│   └── useFetch.js
├── layouts/             # Page layouts
│   ├── DefaultLayout.vue
│   └── AuthLayout.vue
├── modules/             # Functional modules
│   ├── auth/
│   │   ├── components/
│   │   ├── composables/
│   │   ├── stores/
│   │   └── views/
│   └── dashboard/
├── plugins/             # Vue plugins
├── router/
│   ├── index.js
│   └── guards.js
├── stores/              # Global Pinia stores
├── types/               # TypeScript types
├── utils/               # Pure utilities
└── views/               # Pages/Routes

23. Quando usare v-if rispetto a v-show?

La scelta tra v-if e v-show dipende dalla frequenza dei toggle.

javascript
// v-if: low initial cost, expensive toggle
// Removes/adds element from DOM
// Ideal for: rarely modified conditions

<template>
  <!-- v-if: component not created if not admin -->
  <AdminPanel v-if="user.isAdmin" />

  <!-- v-if with v-else-if for multiple conditions -->
  <LoadingSpinner v-if="isLoading" />
  <ErrorMessage v-else-if="error" :message="error" />
  <DataDisplay v-else :data="data" />
</template>

// v-show: higher initial cost, fast toggle
// Uses display: none
// Ideal for: frequent toggles

<template>
  <!-- v-show: always rendered, frequent toggle -->
  <Tooltip v-show="isHovered">
    Contextual information
  </Tooltip>

  <!-- Accordion menu with frequent toggle -->
  <div v-show="isExpanded" class="accordion-content">
    {{ content }}
  </div>
</template>

24. Come ottimizzare le liste con v-for?

L'ottimizzazione delle liste è cruciale per le performance con molti elementi.

javascript
// Always use :key with a unique stable identifier
<template>
  <!--Unique and stable key -->
  <li v-for="item in items" :key="item.id">
    {{ item.name }}
  </li>

  <!--Index as key (reordering issues) -->
  <li v-for="(item, index) in items" :key="index">
    {{ item.name }}
  </li>
</template>

// Filtering and sorting: use computed
<script setup>
import { computed } from 'vue'

const sortedAndFilteredItems = computed(() => {
  return items.value
    .filter(item => item.isActive)
    .sort((a, b) => a.name.localeCompare(b.name))
})
</script>

// Virtualization for very long lists
<script setup>
import { useVirtualList } from '@vueuse/core'

const { list, containerProps, wrapperProps } = useVirtualList(
  largeList,
  { itemHeight: 50 }
)
</script>

<template>
  <div v-bind="containerProps" style="height: 400px; overflow: auto">
    <div v-bind="wrapperProps">
      <div v-for="item in list" :key="item.data.id">
        {{ item.data.name }}
      </div>
    </div>
  </div>
</template>

25. Spiega il pattern Renderless component

I componenti renderless incapsulano la logica senza imporre una struttura HTML.

components/MouseTracker.vuejavascript
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const x = ref(0)
const y = ref(0)

const updatePosition = (event) => {
  x.value = event.clientX
  y.value = event.clientY
}

onMounted(() => {
  window.addEventListener('mousemove', updatePosition)
})

onUnmounted(() => {
  window.removeEventListener('mousemove', updatePosition)
})

// Expose state via slot
defineExpose({ x, y })
</script>

<template>
  <!-- Slot with props: parent decides the rendering -->
  <slot :x="x" :y="y" />
</template>
javascript
// Usage: full control over rendering
<template>
  <MouseTracker v-slot="{ x, y }">
    <div class="cursor-display">
      Position: {{ x }}, {{ y }}
    </div>
  </MouseTracker>

  <MouseTracker v-slot="{ x, y }">
    <svg>
      <circle :cx="x" :cy="y" r="10" fill="red" />
    </svg>
  </MouseTracker>
</template>

Questo pattern separa completamente la logica dalla presentazione, massimizzando il riuso.

Conclusione

Queste 25 domande coprono i concetti essenziali valutati nei colloqui Vue.js:

  • Reattività: ref, reactive, computed, watch
  • Composition API: composable, script setup, TypeScript
  • Performance: lazy loading, virtualizzazione, ottimizzazioni
  • Vue Router: guard, props, navigazione
  • Pinia: store, persistenza, action asincrone
  • Best practice: struttura, convenzioni, pattern avanzati

Una preparazione efficace unisce comprensione teorica e pratica del codice. Ogni concetto trattato qui può diventare oggetto di domande di approfondimento durante il colloquio.

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Tag

#vue.js
#interview
#frontend
#javascript
#technical questions

Condividi

Articoli correlati