frontend / vue / 01_vue_overview.md

Vue: the mental model

5 interview angles 4 min read source

Vue: the mental model

Baseline: Vue 3.5 stable, 3.6 in RC. See ../../STACK_BASELINE.md.

The entry point for this folder — what Vue is, the single-file component, and the reactivity model everything else builds on. Depth lives in the numbered siblings; see README.md for the map.

What Vue is

A progressive framework: it works as a script tag on one page, and as a full application framework with an official router, store and meta-framework. That range is deliberate and is why it shows up both in enterprise admin panels and in incrementally-modernised legacy apps.

Unlike React, Vue ships opinions. Routing is Vue Router, state is Pinia, SSR is Nuxt, styling is scoped <style> in the component file. You spend no time assembling a stack.

The single-file component

<script setup lang="ts">
import { ref, computed } from 'vue';

const count = ref(0);
const doubled = computed(() => count.value * 2);
</script>

<template>
  <button @click="count++">{{ count }} / {{ doubled }}</button>
</template>

<style scoped>
button { font-weight: 600; }
</style>

Three blocks in one file: logic, markup, styles scoped to this component. The .vue file is compiled — nothing here is interpreted at runtime, and the compiler uses the template’s structure to optimise updates. See 19_compiler_and_rendering.md.

<script setup> is the current authoring style. Top-level bindings are automatically exposed to the template, there is no return object, and imported components need no components: registration. Anything you read that uses export default { setup() { ... return { ... } } } is the older, more verbose form.

Reactivity

This is the concept the rest of Vue hangs off. A ref is a box whose .value is tracked; reading it inside a render function, computed or watch registers a dependency, and writing it re-runs whatever read it.

const count = ref(0);          // .value in script, auto-unwrapped in template
const user = reactive({ name: 'Ada' });   // proxy; access properties directly

Three consequences worth internalising early:

  • You mutate state directly. items.value.push(x) is correct. The proxy sees the mutation. This is the opposite of React, where identity change is how updates are detected.
  • There are no dependency arrays. Vue records what was read; you never declare it. See 04_computed_watch_watcheffect.md.
  • Only components that read the changed value re-render. A parent re-rendering does not cascade to children whose props did not change.

Use ref for everything, including objects. reactive loses reactivity when destructured or reassigned, which is the trap people hit in week one — see 03_ref_vs_reactive.md and 02_reactivity_internals.md.

Templates

Templates are HTML plus directives. The essential set:

<p v-if="user">{{ user.name }}</p>
<p v-else>Not signed in</p>

<li v-for="item in items" :key="item.id">{{ item.label }}</li>

<input v-model.trim="query" :disabled="loading" @keyup.enter="search" />
<form @submit.prevent="save">

: is v-bind, @ is v-on, and modifiers like .prevent, .trim, .enter handle the boilerplate that React makes you write by hand. {{ }} interpolation is always escaped; only v-html is unsafe. Full treatment in 18_template_syntax_and_directives.md.

Component communication

Direction Mechanism
Parent to child props — defineProps<{ id: number }>()
Child to parent events — defineEmits<{ select: [id: number] }>()
Two-way defineModel()
Parent to any descendant provide / inject
Parent supplies markup slots, including scoped slots
Across the app a Pinia store

defineProps, defineEmits, defineModel and defineExpose are compiler macros, not imports — they are erased at build time, which is why they must be called at the top level of <script setup>.

Details: 07_props_emits_vmodel.md, 08_slots_and_scoped_slots.md, 09_provide_inject.md, 10_pinia.md.

Lifecycle

onMounted(() => { /* DOM exists */ });
onUnmounted(() => { /* clean up listeners, timers, subscriptions */ });

The Composition API registers hooks by calling them during setup, so they must be called synchronously — registering onMounted inside an await or a callback silently does nothing. Effects created by watch/watchEffect inside a component are disposed automatically on unmount. See 06_lifecycle_hooks.md.

Logic reuse

A composable: a plain function using reactivity APIs, named useSomething, returning refs and functions. There are no rules-of-hooks constraints because it is not identified by call order — it is just a function call. VueUse is the standard library of them. See 16_composables.md.

Starting a project

npm create vue@latest        # official scaffolder: TS, router, Pinia, Vitest, ESLint
npx nuxi@latest init my-app  # Nuxt, when you need SSR/SSG and server routes

Vite is the build tool in both cases — same author as Vue, and the reason Vue’s dev experience is fast by default. Add vue-tsc --noEmit to CI, because Vite strips types without checking them. See 17_typescript_with_vue.md.

Interview angle

  • “What is Vue’s reactivity system?” - Proxy-based dependency tracking. Reading a reactive value inside an effect registers it; writing re-runs the effects that read it. That is why there are no dependency arrays and no manual memoization.
  • “Why can you mutate state in Vue but not React?” - Vue detects change through the proxy, React through reference identity. arr.push(x) is correct Vue and a no-op in React.
  • ref or reactive?” - ref for everything. It works for primitives, survives destructuring and reassignment, and keeps one consistent access pattern. reactive breaks reactivity the moment someone destructures it.
  • “Composition API or Options API?” - Composition with <script setup> for new code: logic composes into testable functions, TypeScript inference works properly, and tree-shaking improves. Options API still ships and is fine to maintain.
  • “What is <script setup> doing?” - it is compiled into a setup() function with template bindings inlined. The define* calls are compile-time macros, not runtime functions, which is why they cannot be called conditionally.