Questions d'entretien Vue.js essentielles : 25 questions pour réussir
Préparez vos entretiens Vue.js avec ces 25 questions essentielles. De la réactivité aux composables, maîtrisez les concepts clés pour décrocher le poste.

Les entretiens Vue.js évaluent bien plus que la syntaxe du framework. Les recruteurs cherchent à comprendre la maîtrise du système de réactivité, l'organisation du code avec la Composition API, et la capacité à résoudre des problèmes concrets de performance et d'architecture.
Chaque question est accompagnée d'une réponse détaillée et d'exemples de code. Pour les entretiens techniques, il est recommandé de pratiquer en expliquant les concepts à voix haute, comme lors d'un vrai entretien.
Questions fondamentales sur Vue.js
1. Quelle est la différence entre ref et reactive dans Vue 3 ?
Cette question teste la compréhension du système de réactivité, fondamental dans Vue 3. La différence principale réside dans les types de données gérés et la syntaxe d'accès.
ref crée une référence réactive pour les valeurs primitives (string, number, boolean) et nécessite .value pour accéder à la valeur dans le script. reactive crée un proxy réactif pour les objets et permet un accès direct aux propriétés.
// Exemple comparatif ref vs reactive
import { ref, reactive } from 'vue'
// ref: pour les primitives
// Nécessite .value dans le script
const count = ref(0)
count.value++ // Accès avec .value
// reactive: pour les objets complexes
// Accès direct aux propriétés
const user = reactive({
name: 'Alice',
age: 25
})
user.age++ // Pas de .value nécessaire
// Attention: reactive perd la réactivité si réassigné
// user = { name: 'Bob', age: 30 } // ❌ Casse la réactivité
Object.assign(user, { name: 'Bob', age: 30 }) // ✅ CorrectLa règle générale : utiliser ref pour les valeurs simples et reactive pour les objets structurés avec plusieurs propriétés liées.
2. Comment fonctionne le système de réactivité de Vue 3 ?
Vue 3 utilise les Proxies JavaScript (ES6) pour intercepter les opérations sur les objets réactifs. Contrairement à Vue 2 qui utilisait Object.defineProperty, cette approche permet de détecter l'ajout et la suppression de propriétés dynamiquement.
// Démonstration simplifiée du principe de réactivité
// Vue utilise les Proxies pour tracker les dépendances
const handler = {
// Interception de la lecture
get(target, key, receiver) {
track(target, key) // Enregistre la dépendance
return Reflect.get(target, key, receiver)
},
// Interception de l'écriture
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
trigger(target, key) // Déclenche les mises à jour
return result
}
}
// Création d'un proxy réactif
const reactiveObject = new Proxy(originalObject, handler)Les avantages des Proxies incluent : détection des nouvelles propriétés, support des Map/Set, et meilleure performance sur les objets volumineux.
3. Expliquez la différence entre computed et watch
Les computed et watch répondent à des besoins différents dans la gestion de la réactivité.
Computed : calcule une valeur dérivée à partir d'autres données réactives. Les valeurs sont mises en cache et ne se recalculent que lorsque les dépendances changent. Idéal pour les transformations de données.
Watch : exécute des effets secondaires en réponse aux changements. Utile pour les appels API, les interactions avec le DOM, ou les opérations asynchrones.
// Comparaison computed vs watch
import { ref, computed, watch } from 'vue'
const firstName = ref('Jean')
const lastName = ref('Dupont')
// computed: valeur dérivée avec cache
// Se recalcule uniquement si firstName ou lastName change
const fullName = computed(() => {
console.log('Calcul du nom complet') // Appelé une seule fois
return `${firstName.value} ${lastName.value}`
})
// Accès multiple = une seule exécution (cache)
console.log(fullName.value) // "Jean Dupont"
console.log(fullName.value) // Pas de recalcul
// watch: effet secondaire sans cache
// Exécuté à chaque changement
watch(firstName, async (newName, oldName) => {
// Effet secondaire: appel API
await saveToServer({ firstName: newName })
console.log(`Nom changé de ${oldName} à ${newName}`)
})4. Qu'est-ce que le Virtual DOM et comment Vue l'utilise ?
Le Virtual DOM est une représentation JavaScript légère du DOM réel. Vue maintient un arbre virtuel en mémoire et calcule les différences (diffing) entre l'état précédent et le nouvel état pour appliquer uniquement les changements nécessaires au DOM réel.
// Représentation conceptuelle du Virtual DOM
// Vue crée cette structure en interne
const vnode = {
type: 'div',
props: {
class: 'container',
id: 'app'
},
children: [
{
type: 'h1',
props: {},
children: 'Titre'
},
{
type: 'p',
props: {},
children: 'Contenu du paragraphe'
}
]
}
// Lors d'un changement, Vue compare les vnodes
// et met à jour uniquement les éléments modifiésLes optimisations de Vue 3 incluent : hoisting des nœuds statiques, patch flags pour identifier les types de changements, et tree-shaking du compilateur.
5. Comment gérer la communication entre composants non liés ?
Plusieurs patterns existent pour la communication entre composants sans relation parent-enfant directe.
// Solution 1: Event Bus (petites applications)
// eventBus.js
import { ref } from 'vue'
const bus = ref(new Map())
export function useEventBus() {
// Émettre un événement
const emit = (event, payload) => {
const callbacks = bus.value.get(event) || []
callbacks.forEach(cb => cb(payload))
}
// Écouter un événement
const on = (event, callback) => {
if (!bus.value.has(event)) {
bus.value.set(event, [])
}
bus.value.get(event).push(callback)
}
return { emit, on }
}// Solution 2: Provide/Inject pour les composants imbriqués
// Ancêtre
import { provide, ref } from 'vue'
const sharedState = ref('valeur partagée')
provide('stateKey', sharedState)
// Descendant (n'importe quel niveau)
import { inject } from 'vue'
const state = inject('stateKey')Pour les applications plus complexes, Pinia reste la solution recommandée pour la gestion d'état globale.
Questions sur la Composition API
6. Quels sont les avantages de la Composition API par rapport à l'Options API ?
La Composition API offre plusieurs avantages structurels par rapport à l'Options API traditionnelle.
Organisation par fonctionnalité : le code lié à une même fonctionnalité est regroupé, contrairement à l'Options API qui sépare par type (data, methods, computed).
Réutilisation via composables : extraction facile de la logique dans des fonctions réutilisables.
Meilleur support TypeScript : inférence de types naturelle sans décorateurs.
// Options API: code fragmenté par 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 groupé par fonctionnalité
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. Comment créer un composable réutilisable ?
Les composables sont des fonctions qui encapsulent de la logique réactive. Les conventions incluent : préfixe use, retour d'un objet avec l'état et les méthodes, et gestion du nettoyage.
import { ref, watch } from 'vue'
// Composable pour synchroniser un état avec localStorage
export function useLocalStorage(key, defaultValue) {
// Récupérer la valeur initiale depuis localStorage
const storedValue = localStorage.getItem(key)
const data = ref(
storedValue ? JSON.parse(storedValue) : defaultValue
)
// Synchroniser les changements vers localStorage
watch(
data,
(newValue) => {
if (newValue === null) {
localStorage.removeItem(key)
} else {
localStorage.setItem(key, JSON.stringify(newValue))
}
},
{ deep: true } // Observer les objets imbriqués
)
return data
}
// Utilisation dans un composant
const theme = useLocalStorage('theme', 'light')
const userPrefs = useLocalStorage('prefs', { notifications: true })Les composables suivent la convention useXxx pour indiquer leur nature réutilisable. Cette convention améliore la lisibilité et facilite l'identification des dépendances réactives.
8. Expliquez watchEffect vs watch
watchEffect et watch servent à réagir aux changements, mais avec des approches différentes.
watchEffect : exécute immédiatement et re-exécute automatiquement quand ses dépendances réactives changent. Les dépendances sont trackées automatiquement.
watch : observe des sources spécifiques et fournit les anciennes et nouvelles valeurs. Plus de contrôle sur le déclenchement.
// Comparaison watchEffect vs watch
import { ref, watch, watchEffect } from 'vue'
const userId = ref(1)
const userData = ref(null)
// watchEffect: tracking automatique
// S'exécute immédiatement
watchEffect(async () => {
// userId est automatiquement tracké
const response = await fetch(`/api/users/${userId.value}`)
userData.value = await response.json()
})
// watch: sources explicites avec anciennes valeurs
watch(userId, async (newId, oldId) => {
console.log(`User changé de ${oldId} à ${newId}`)
const response = await fetch(`/api/users/${newId}`)
userData.value = await response.json()
}, {
immediate: true // Exécuter immédiatement comme watchEffect
})
// watchEffect avec cleanup
watchEffect((onCleanup) => {
const controller = new AbortController()
fetch(`/api/users/${userId.value}`, {
signal: controller.signal
}).then(/* ... */)
// Cleanup: annuler la requête précédente
onCleanup(() => controller.abort())
})9. Comment gérer les props avec TypeScript dans script setup ?
La syntaxe <script setup> offre une intégration TypeScript native avec defineProps et withDefaults.
<script setup lang="ts">
// Interface pour les props
interface Props {
title: string
count?: number
items?: string[]
onSubmit?: (data: FormData) => void
}
// defineProps avec typage générique
// withDefaults pour les valeurs par défaut
const props = withDefaults(defineProps<Props>(), {
count: 0,
items: () => [], // Factory pour les objets/tableaux
onSubmit: undefined
})
// Les props sont automatiquement typées
console.log(props.title) // string
console.log(props.count) // number
// defineEmits avec typage
const emit = defineEmits<{
(e: 'update', value: number): void
(e: 'delete', id: string): void
}>()
// Utilisation typée des emits
const handleUpdate = () => {
emit('update', props.count + 1)
}
</script>Prêt à réussir tes entretiens Vue.js / Nuxt.js ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Questions sur les performances
10. Quelles techniques d'optimisation des performances connaissez-vous ?
Vue 3 offre plusieurs mécanismes d'optimisation des performances.
<template>
<div v-once>
<!-- Ce contenu ne sera jamais re-rendu -->
<ComplexStaticComponent />
</div>
</template>
// 2. v-memo: mémorisation conditionnelle
<template>
<div v-for="item in list" :key="item.id" v-memo="[item.id, item.selected]">
<!-- Re-rendu uniquement si id ou selected change -->
{{ item.name }}
</div>
</template>
// 3. shallowRef/shallowReactive: réactivité superficielle
import { shallowRef, triggerRef } from 'vue'
// Ne track que le remplacement de la ref, pas les mutations internes
const largeList = shallowRef([/* milliers d'éléments */])
// Forcer la mise à jour après mutation
largeList.value.push(newItem)
triggerRef(largeList) // Déclencher manuellement le re-renduimport { defineAsyncComponent } from 'vue'
const HeavyComponent = defineAsyncComponent({
loader: () => import('./HeavyComponent.vue'),
loadingComponent: LoadingSpinner,
delay: 200, // Délai avant d'afficher le loading
errorComponent: ErrorDisplay,
timeout: 3000
})
// 5. KeepAlive pour le cache des composants
<template>
<KeepAlive :include="['Dashboard', 'UserProfile']" :max="10">
<component :is="currentView" />
</KeepAlive>
</template>11. Comment éviter les re-rendus inutiles ?
Les re-rendus inutiles peuvent impacter les performances. Plusieurs stratégies permettent de les minimiser.
// Problème: fonction créée à chaque rendu
<template>
<!-- ❌ Nouvelle fonction à chaque rendu -->
<ChildComponent @click="() => handleClick(item.id)" />
</template>
// Solution: utiliser une méthode ou une ref
<script setup>
const handleItemClick = (id) => {
// Logique de traitement
}
</script>
<template>
<!-- ✅ Référence stable -->
<ChildComponent @click="handleItemClick(item.id)" />
</template>// Utilisation de computed pour les calculs coûteux
import { computed } from 'vue'
// ❌ Recalculé à chaque rendu
const getFilteredItems = () => {
return items.value.filter(/* logique complexe */)
}
// ✅ Mis en cache, recalculé uniquement si items change
const filteredItems = computed(() => {
return items.value.filter(/* logique complexe */)
})12. Expliquez le lazy loading des composants et routes
Le lazy loading permet de charger le code à la demande, réduisant la taille du bundle initial.
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
// Chargement immédiat (bundle principal)
component: () => import('@/views/Home.vue')
},
{
path: '/dashboard',
// Chunk séparé avec nom personnalisé
component: () => import(
/* webpackChunkName: "dashboard" */
'@/views/Dashboard.vue'
),
// Lazy loading des routes enfants
children: [
{
path: 'analytics',
component: () => import('@/views/Analytics.vue')
}
]
},
{
path: '/admin',
// Préchargement sur hover du lien
component: () => import('@/views/Admin.vue'),
meta: { prefetch: true }
}
]
})
export default routerQuestions sur le routage avec Vue Router
13. Comment protéger les routes avec des guards ?
Les navigation guards permettent de contrôler l'accès aux routes.
import { createRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const router = createRouter({
routes: [
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true, roles: ['admin', 'user'] }
}
]
})
// Guard global: vérifie l'authentification
router.beforeEach(async (to, from, next) => {
const auth = useAuthStore()
// Route publique
if (!to.meta.requiresAuth) {
return next()
}
// Vérifier l'authentification
if (!auth.isAuthenticated) {
return next({
path: '/login',
query: { redirect: to.fullPath }
})
}
// Vérifier les rôles si spécifiés
if (to.meta.roles && !to.meta.roles.includes(auth.user.role)) {
return next('/unauthorized')
}
next()
})
// Guard au niveau du composant
export default {
beforeRouteEnter(to, from, next) {
// Pas accès à this ici
next(vm => {
// Accès à l'instance du composant via vm
vm.loadData()
})
},
beforeRouteLeave(to, from, next) {
// Confirmer avant de quitter si formulaire modifié
if (this.hasUnsavedChanges) {
const answer = confirm('Quitter sans sauvegarder ?')
next(answer)
} else {
next()
}
}
}14. Comment passer des props aux routes ?
Vue Router permet de découpler les composants des paramètres de route.
// Configuration des routes avec props
const routes = [
{
path: '/user/:id',
component: UserProfile,
// Mode boolean: passe les params comme props
props: true
},
{
path: '/search',
component: SearchResults,
// Mode fonction: transformation personnalisée
props: (route) => ({
query: route.query.q,
page: parseInt(route.query.page) || 1,
filters: route.query.filters?.split(',') || []
})
},
{
path: '/static',
component: StaticPage,
// Mode objet: props statiques
props: { sidebar: true, theme: 'dark' }
}
]<script setup>
// Les props sont injectées automatiquement
defineProps<{
id: string
}>()
</script>
// SearchResults.vue
<script setup>
defineProps<{
query: string
page: number
filters: string[]
}>()
</script>Questions sur Pinia et la gestion d'état
15. Quelles sont les différences entre Pinia et Vuex ?
Pinia est le gestionnaire d'état officiel pour Vue 3, remplaçant Vuex avec une API simplifiée.
| Caractéristique | Vuex | Pinia | |----------------|------|-------| | Mutations | Requises | Non nécessaires | | Modules | Configuration complexe | Stores indépendants | | TypeScript | Support limité | Natif et complet | | API | Options | Composition + Options | | DevTools | Support | Support complet |
// Store Pinia avec 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 (fonctions directes)
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. Comment persister l'état d'un store Pinia ?
La persistance permet de conserver l'état entre les sessions utilisateur.
import { watch } from 'vue'
export function createPersistedState(options = {}) {
const {
key = 'pinia',
storage = localStorage,
paths = null
} = options
return ({ store }) => {
// Restaurer l'état au démarrage
const savedState = storage.getItem(`${key}-${store.$id}`)
if (savedState) {
store.$patch(JSON.parse(savedState))
}
// Persister les changements
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'] // Persister seulement ces clés
}))Éviter de persister des données sensibles (tokens, mots de passe) dans localStorage. Utiliser des solutions sécurisées comme les cookies httpOnly pour les tokens d'authentification.
Questions avancées
17. Comment implémenter un système de plugins Vue ?
Les plugins permettent d'étendre Vue avec des fonctionnalités globales.
export const AnalyticsPlugin = {
install(app, options = {}) {
const { trackingId, debug = false } = options
// Injection globale disponible dans tous les composants
const analytics = {
trackEvent(category, action, label) {
if (debug) {
console.log('Analytics:', { category, action, label })
}
// Logique d'envoi vers le service analytics
window.gtag?.('event', action, {
event_category: category,
event_label: label
})
},
trackPage(path) {
window.gtag?.('config', trackingId, { page_path: path })
}
}
// Rendre disponible via inject
app.provide('analytics', analytics)
// Ajouter une propriété globale (déconseillé en Composition API)
app.config.globalProperties.$analytics = analytics
// Directive personnalisée pour le tracking au clic
app.directive('track', {
mounted(el, binding) {
el.addEventListener('click', () => {
analytics.trackEvent('click', binding.value, el.textContent)
})
}
})
// Tracking automatique des changements de route
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. Expliquez les Render Functions et leur utilité
Les render functions offrent un contrôle total sur le rendu, utile pour les composants hautement dynamiques.
import { h } from 'vue'
// Composant fonctionnel avec render function
export const DynamicHeading = {
props: {
level: {
type: Number,
default: 1,
validator: (v) => v >= 1 && v <= 6
}
},
setup(props, { slots }) {
// h() crée un vnode
// Arguments: type, props, children
return () => h(
`h${props.level}`,
{ class: 'dynamic-heading' },
slots.default?.()
)
}
}
// Composant avec logique conditionnelle complexe
export const ConditionalWrapper = {
props: ['condition', 'wrapper'],
setup(props, { slots }) {
return () => {
if (props.condition) {
return h(props.wrapper, null, slots.default?.())
}
return slots.default?.()
}
}
}
// Utilisation
<DynamicHeading :level="2">Titre niveau 2</DynamicHeading>
<ConditionalWrapper :condition="isLink" wrapper="a">
Contenu conditionnel
</ConditionalWrapper>19. Comment tester les composants Vue avec Vitest ?
Les tests unitaires valident le comportement isolé des composants.
import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import Counter from '../Counter.vue'
describe('Counter', () => {
it('affiche la valeur initiale', () => {
const wrapper = mount(Counter, {
props: { initialValue: 5 }
})
expect(wrapper.text()).toContain('5')
})
it('incrémente la valeur au clic', async () => {
const wrapper = mount(Counter)
await wrapper.find('button.increment').trigger('click')
expect(wrapper.text()).toContain('1')
})
it('émet un événement lors du changement', 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('appelle le service au submit', async () => {
const mockSubmit = vi.fn()
const wrapper = mount(Counter, {
global: {
provide: {
submitService: mockSubmit
}
}
})
await wrapper.find('form').trigger('submit')
expect(mockSubmit).toHaveBeenCalled()
})
})20. Comment gérer les erreurs globalement dans Vue ?
Vue 3 offre plusieurs mécanismes pour capturer et gérer les erreurs.
import { createApp } from 'vue'
const app = createApp(App)
// Handler global pour les erreurs de composants
app.config.errorHandler = (err, instance, info) => {
// err: l'erreur
// instance: l'instance du composant
// info: chaîne décrivant où l'erreur s'est produite
console.error('Erreur Vue:', err)
console.error('Composant:', instance?.$options?.name)
console.error('Info:', info)
// Envoyer à un service de monitoring
errorTracker.captureException(err, {
component: instance?.$options?.name,
info
})
}
// Handler pour les avertissements (dev uniquement)
app.config.warnHandler = (msg, instance, trace) => {
console.warn('Avertissement Vue:', msg)
}// Composant ErrorBoundary
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
// Capture les erreurs des composants enfants
onErrorCaptured((err, instance, info) => {
error.value = {
message: err.message,
component: instance?.$options?.name,
info
}
// Retourner false pour arrêter la propagation
return false
})
const retry = () => {
error.value = null
}
</script>
<template>
<div v-if="error" class="error-boundary">
<h2>Une erreur s'est produite</h2>
<p>{{ error.message }}</p>
<button @click="retry">Réessayer</button>
</div>
<slot v-else />
</template>Prêt à réussir tes entretiens Vue.js / Nuxt.js ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Questions sur les bonnes pratiques
21. Quelles conventions de nommage suivre dans Vue ?
Les conventions de nommage améliorent la lisibilité et la maintenabilité du code.
// Nommage des composants
// PascalCase pour les fichiers et le nom
// BaseButton.vue, AppHeader.vue, TheNavbar.vue
// Props: camelCase en JS, kebab-case en template
defineProps<{
userName: string // JS
isActive: boolean // JS
}>()
// <UserCard :user-name="name" :is-active="active" />
// Events: camelCase avec préfixe action
const emit = defineEmits<{
(e: 'updateValue', value: string): void // ✅
(e: 'submit'): void // ✅
(e: 'value-updated'): void // ❌ Éviter
}>()
// Composables: préfixe use
// useAuth.js, useFetch.js, useLocalStorage.js
// Stores Pinia: préfixe use + Store suffix
// useUserStore, useCartStore, useSettingsStore
// Constantes: SCREAMING_SNAKE_CASE
const MAX_RETRY_COUNT = 3
const API_BASE_URL = '/api/v1'22. Comment structurer un projet Vue à grande échelle ?
Une structure modulaire facilite la navigation et la maintenance.
src/
├── assets/ # Fichiers statiques
├── components/
│ ├── ui/ # Composants génériques (Button, Modal)
│ └── common/ # Composants métier réutilisables
├── composables/ # Logique réutilisable
│ ├── useAuth.js
│ └── useFetch.js
├── layouts/ # Layouts de page
│ ├── DefaultLayout.vue
│ └── AuthLayout.vue
├── modules/ # Modules fonctionnels
│ ├── auth/
│ │ ├── components/
│ │ ├── composables/
│ │ ├── stores/
│ │ └── views/
│ └── dashboard/
├── plugins/ # Plugins Vue
├── router/
│ ├── index.js
│ └── guards.js
├── stores/ # Stores Pinia globaux
├── types/ # Types TypeScript
├── utils/ # Utilitaires purs
└── views/ # Pages/Routes23. Quand utiliser v-if vs v-show ?
Le choix entre v-if et v-show dépend de la fréquence de basculement.
// v-if: coût initial faible, basculement coûteux
// Retire/ajoute l'élément du DOM
// Idéal pour: conditions rarement modifiées
<template>
<!-- v-if: le composant n'est pas créé si non admin -->
<AdminPanel v-if="user.isAdmin" />
<!-- v-if avec v-else-if pour les conditions multiples -->
<LoadingSpinner v-if="isLoading" />
<ErrorMessage v-else-if="error" :message="error" />
<DataDisplay v-else :data="data" />
</template>
// v-show: coût initial plus élevé, basculement rapide
// Utilise display: none
// Idéal pour: basculements fréquents
<template>
<!-- v-show: toujours rendu, toggle fréquent -->
<Tooltip v-show="isHovered">
Information contextuelle
</Tooltip>
<!-- Menu accordéon avec toggle fréquent -->
<div v-show="isExpanded" class="accordion-content">
{{ content }}
</div>
</template>24. Comment optimiser les listes avec v-for ?
L'optimisation des listes est cruciale pour les performances avec de nombreux éléments.
// Toujours utiliser :key avec un identifiant unique stable
<template>
<!-- ✅ Key unique et stable -->
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
<!-- ❌ Index comme key (problèmes de réordonnancement) -->
<li v-for="(item, index) in items" :key="index">
{{ item.name }}
</li>
</template>
// Filtrage et tri: utiliser 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>
// Virtualisation pour les très longues listes
<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. Expliquez le pattern de composants sans rendu (Renderless)
Les composants renderless encapsulent la logique sans imposer de structure HTML.
<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)
})
// Exposer l'état via le slot
defineExpose({ x, y })
</script>
<template>
<!-- Slot avec props: le parent décide du rendu -->
<slot :x="x" :y="y" />
</template>// Utilisation: contrôle total sur le rendu
<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>Ce pattern sépare complètement la logique de la présentation, maximisant la réutilisabilité.
Conclusion
Ces 25 questions couvrent les concepts essentiels évalués lors des entretiens Vue.js :
- ✅ Réactivité :
ref,reactive,computed,watch - ✅ Composition API : composables,
script setup, TypeScript - ✅ Performances : lazy loading, virtualisation, optimisations
- ✅ Vue Router : guards, props, navigation
- ✅ Pinia : stores, persistance, actions asynchrones
- ✅ Bonnes pratiques : structure, conventions, patterns avancés
La préparation efficace combine la compréhension théorique avec la pratique du code. Chaque concept abordé ici peut faire l'objet de questions de suivi approfondies lors de l'entretien.
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Tags
Partager
Articles similaires

Vue 3 Composition API : Guide complet pour maîtriser la réactivité
Découvrez Vue 3 Composition API avec ce guide pratique. Apprenez ref, reactive, computed, watch et les composables pour créer des applications Vue performantes.

Nuxt 3 : SSR et génération statique, le guide complet
Maîtrisez le SSR et la génération statique avec Nuxt 3. De useFetch à generateRoutes, découvrez comment optimiser les performances de vos applications Vue.

NestJS en entretien : Guards, Interceptors et Architecture modulaire
Les questions d'entretien NestJS sur les Guards, Interceptors et l'architecture modulaire, avec des exemples de code concrets et des explications techniques.