Vue next to React and Angular
Asked in interviews
AskedHow does Vue.js differ from React and Angular?ref() holds any value. Script reads it through .value, and the template unwraps it. reactive() turns an object into a Proxy, and destructuring it loses reactivity.nextTick().<script setup>. The Options API, with data, methods and this, still works and still comes up in interviews.Asked in interviews
AskedHow does Vue.js differ from React and Angular?createApp(App).mount('#app') install(app, options)t('hello', { name })TipVue DevTools shows the component tree, props, state and Pinia stores. Add it as a browser extension or a Vite plugin.
npm create vue@latestA Vite projectlang="ts"TrapVite strips TypeScript without checking it, so a type error still builds unless you run vue-tsc.
<script setup>app.component('X', X)TrapA global component can't be tree-shaken, so it ships even when no page uses it.
data(), methods, all via thisref(), watch(), grouped by featuresetup() by handsetup(props, context)Returns what the template usesTrapDestructuring props in setup() loses reactivity. Read props.user, or use toRefs(props).
v-if="ok"Each toggle rebuilds itv-show="ok"Cheap toggle, no v-else@click="open = !open"TrapIn Vue 3, v-if runs before v-for on the same element, so it can't see the loop item.
{{ msg }}:href="url"@click="save"v-html="html"Only on sanitized HTMLapp.directive('focus', vFocus)TipUse a custom directive only for low-level DOM work on plain elements, never on components.
v-for="item in items":key="item.id"A new key rebuilds itv-model on the input, then loop over a computedTrapAn index key follows the position, not the item. When the list changes, state sticks to the wrong row.
@submit.prevent@click.stopv-model.trimv-model.numberv-model.lazy@blur="check('email')" Recheck all on submit@dragover.prevent Needed, or drops are refused:class="{ active: on }"<style scoped>Adds a data-v- attribute:deep(.item)TrapA parent's scoped styles reach a child's root element, but nothing inside it.
const n = ref(0)Read n.value in scriptreactive({ a: 1 })Objects only, no .valueshallowRef(rows) Nested edits need triggerRef()$forceUpdate()A sign of a reactivity bugTrapReplacing or destructuring a reactive() object breaks reactivity. That's why Vue recommends ref() by default.
computed(() => n.value * 2)TrapA computed that reads only Date.now() never updates. It re-runs only when reactive state it reads changes.
watch(q, fn)watch(() => s.id, fn)watchEffect(fn)Runs at once, no old value{ once: true }TrapWatching a reactive() object is deep by default. A deep watcher walks every nested property on each change.
track(), writes trigger()defineProperty, which misses new keysawait nextTick()this.$nextTick() defineProps(['title']) defineProps<Props>() { type: Number, required: true }default: () => []{ count = 0 } stays reactiveTrapProps are read-only. To change one, emit an event and let the parent update it.
defineEmits(['save']) emit('save', item)modelValue in, update:modelValue outdefineModel():title.sync="t"Now v-model:titleTrapComponent events don't bubble. Only the direct parent can listen.
<template #header><slot :item="item" />#default="{ item }"TrapSlot content compiles in the parent's scope, so it can't read the child's data without slot props.
$attrsuseAttrs()inheritAttrs: falsev-bind="$attrs"v-on="$listeners" Gone, now in $attrsTrapWith several root nodes, nothing falls through, and Vue warns. Bind $attrs yourself.
useTemplateRef('el') this.$refs.input defineExpose({ reset })this.$parentA last resort. It couples them.TrapA template ref is null until mount. Read it in onMounted(), not in setup code.
before hookcreatedonMounted(fn)Browser only, not in SSRbeforeDestroyNow beforeUnmountTrapRegister hooks synchronously in setup. A hook inside a setTimeout has no component to attach to.
onBeforeUnmount(fn) Clear timers and listeners, destroy librariessocket.close()Set a flag, or it reconnectsTipVue stops the watchers made in setup by itself. One made after an await needs the stop function watch() returns.
use function. Each call makes new state.const { count } = useCounter()mixins: [myMixin]Unclear sources, name clasheswatch(() => p.id, load)With immediate: trueonWatcherCleanup(fn) <Suspense>Still experimentalonErrorCaptured(fn) app.config.errorHandler TrapVue never sees an error in your own setTimeout or an unawaited promise. Catch those yourself.
createWebHistory() Needs a server fallback<router-link to="/"><router-view />router.push('/home') router.replace('/') TrapPass params with a route name. Vue Router ignores params next to a path.
path: '/user/:id'Read it as route.params.idchildren array, plus a <router-view> in the parent() => import('./About.vue') TrapGoing from /user/1 to /user/2 reuses the component, so mounted won't run again. Watch route.params.
router.beforeEach(fn) Return false to cancelonBeforeRouteLeave(fn) meta: { auth: true }Read to.meta.auth<component :is="tab"><KeepAlive>onActivated() runs each time it shows<Teleport to="body">Escapes a parent's transformTrapWithout max, KeepAlive caches every instance it sees, which can leak memory.
On insert, Vue adds v-enter-from and v-enter-active. A frame later it swaps v-enter-from for v-enter-to. Both go when the transition ends.
<Transition name="fade"><TransitionGroup> .list-move slides moved itemsTrapEvery child of <Transition needs a unique key, and it has no mode.
v-oncev-memo="[id, sel]"TipPass each list item an :active flag, not the shared activeId. Then only the items whose flag flips update.
TrapAn incomplete v-memo list skips updates that should happen, and nothing warns you.
defineAsyncComponent(fn) IntersectionObserver Watch a sentinel after the listlodash-esTrapA bare () => import() works only for routes. Elsewhere, use define.
h('div', props, kids)TrapRender functions miss the template compiler's optimizations, so use templates by default.
mount(Comp, { props })shallowMount(Comp) await w.trigger('click') w.emitted('save') TrapWithout await, the next assertion still sees the old DOM, because Vue updates it later.
vi.mock('axios') await flushPromises()createTestingPinia() In global.pluginsTrapA module mock ties the test to the HTTP client. Switch axios to fetch and it breaks.
renderToString(app) createSSRApp() hydrates itonMounted()useId()TrapRandom values, time zones and a <div> inside a <p> cause hydration mismatches.
defineCustomElement(C) Then customElements.define() new Vue({ render })Vue 3 uses createApp()@vue/compatWarns on each Vue 2 APITipVue 2 reached end of life on December 31, 2023.
TrapThe migration build can't run libraries that rely on Vue 2 internals. Check each one has a Vue 3 version.
Asked in interviews
AskedWhat are the differences between Vue 2 and Vue 3?Nothing matches. Try a shorter word, or clear the search.
With Pro, you can:
Already have Pro? Sign in