Every Nuxt interview starts with Vue, and at senior level the questions stop being "what does computed do" and become "why does this component re-render", "why did this watcher not fire", "how would you design this API so the team cannot misuse it". The interviewer is checking that you understand the machine underneath the templates: a Proxy-based dependency tracker, a scheduler that batches effects, and a compiler that turns templates into render functions with patch flags.
This section covers Vue 3.5, which Nuxt 4 runs on, and names what Vue 3.6 changes. As of September 2026, 3.5.43 is the stable release and 3.6 is at release candidate 9; say "release candidate" in the room unless you have checked the tags that week.
reactive() wraps an object in a Proxy whose get trap records the running effect as a dependency and whose set trap schedules those effects. ref() is an object with a getter/setter on .value doing the same. computed is a lazy effect with a cache flag; watch and the render function are effects too.nextTick() resolves after the flush, which is why tests await it.shouldUpdateComponent check decide).v-memo and v-once are the manual escape hatches.setup (onMounted, onUpdated) never run on the server, window does not exist, and state must not be module-scoped or it leaks between requests. Every page in this section names the SSR angle because Nuxt makes it unavoidable.| Page | The question it prepares you for |
|---|---|
| Reactivity system | "Why did this not update?" and "What is the difference between ref, reactive and shallowRef?" |
| Component design | "Design a component API for the team": props, emits, slots, defineModel, provide/inject, generics |
| Composables | "How do you share logic?" and "What makes a composable SSR-safe?" |
| Rendering and lifecycle | Suspense, Teleport, KeepAlive, async components, render functions, nextTick |
| Patterns and anti-patterns | "Review this component": renderless components, prop drilling, watchers for derived state |
| API styles and migration | Options vs Composition, mixins to composables, Vue 2 to 3, Nuxt 3 to 4 |
| Vue 3.6 and Vapor | "What is coming in Vue?": Vapor mode, alien-signals, what does not change |
The whole reactivity system in thirty lines, which is worth being able to sketch on a whiteboard:
type Effect = () => void
let activeEffect: Effect | null = null
const targetMap = new WeakMap<object, Map<PropertyKey, Set<Effect>>>()
function track(target: object, key: PropertyKey) {
if (!activeEffect) return
let deps = targetMap.get(target)
if (!deps) targetMap.set(target, (deps = new Map()))
let dep = deps.get(key)
if (!dep) deps.set(key, (dep = new Set()))
dep.add(activeEffect)
}
function trigger(target: object, key: PropertyKey) {
targetMap.get(target)?.get(key)?.forEach(effect => effect())
}
export function reactive<T extends object>(obj: T): T {
return new Proxy(obj, {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const ok = Reflect.set(target, key, value, receiver)
trigger(target, key)
return ok
},
})
}
export function effect(fn: Effect) {
const run = () => {
activeEffect = run
try { fn() }
finally { activeEffect = null }
}
run()
}
Real Vue adds a scheduler (effects are queued and deduplicated instead of run synchronously), nested effect stacks, computed with dirty flags, array and collection instrumentation, and, in 3.6, a rewrite of the dependency graph on top of alien-signals. But the shape is exactly this, and being able to draw it is what turns "I know Vue" into "I understand Vue".
reactive(obj) !== obj. Code that holds the original and mutates it bypasses the trap, so nothing updates. The same trap catches people who spread a reactive object ({ ...state }) or destructure it (const { count } = state): the copies are plain values with no dependency tracking. toRefs exists for exactly that case.
computed() with a dirty flag and a microtask-batched scheduler, and prove with console.log that two mutations in one tick run the effect once."Vue wraps state in Proxies. When an effect such as a component's render function runs, every property read goes through the get trap and Vue records that effect as a dependency of that property. When a set trap fires, the effects depending on that property are queued and flushed on the next microtask, so several mutations in one tick produce one re-render. ref does the same through a .value accessor, computed is a lazy, cached effect, and watch is an effect with a callback. The consequences are the things people trip on: a destructured or spread reactive object loses tracking, mutating the raw object bypasses the Proxy, and updates are asynchronous, which is why nextTick exists."
Nothing yet. Notes filed here show up on every page of this section.
The v5 readiness plan
A concrete checklist for making a module and a layer Nuxt 5 ready, and turning the findings into an interview story.
Reactivity system
ref, reactive, shallowRef and computed underneath, watch versus watchEffect and flush timing, effect scopes, and the mistakes that make state stop updating.