The Extra Topics are where the guide explains itself: why the Composition API exists, how reactivity and rendering actually work, and how Vue relates to render functions, web components and animation. Senior interviews live here.
<script setup> compiles to less code).setup() option). The class API was dropped.setup() runs once, so no stale closures, no dependency arrays, no useCallback/useMemo for stability, and no call-order rules; watchers and computed track dependencies automatically.track() on read records the running effect in a dependency set; trigger() on write re-runs the effects; whenDepsChange (an effect) wraps the update. Vue does this at runtime via Proxies, as opposed to compile-time reactivity (Svelte).onRenderTracked/onRenderTriggered on components; onTrack/onTrigger options on computed and watch.shallowRef (the useImmer example), state machines (XState), RxJS (@vueuse/rxjs).ref is a signal, computed a derived value, watchEffect an effect; the .value API exists because a getter/setter pair is needed to track access.TEXT, CLASS, PROPS… tell the runtime what can change), tree flattening (block nodes track only dynamic descendants; v-if/v-for open new blocks), and hydration skips static content using the same information.h(type, props?, children?); props can be omitted when children are given; children are a string, an array, or a slots object of functions. Vnodes must be unique: do not put the same vnode instance twice in a tree; use a factory.setup() returns a function, or the render option. JSX/TSX via @vitejs/plugin-vue-jsx (or jsxImportSource: 'vue').v-if → ternary, v-for → map, v-on → onClick, modifiers via withModifiers(fn, ['self']) or onClickCapture/onClickOnce; components are imported and passed as type, or resolveComponent('Name') for globally registered ones; slots: render with slots.default?.(), pass as an object of functions (scoped slots receive props); built-ins must be imported (h(KeepAlive, …)); v-model → modelValue plus 'onUpdate:modelValue'; custom directives via withDirectives(h(...), [[vPin, value, arg, modifiers]]) and resolveDirective; template refs via ref (object or function).(props, { slots, emit, attrs }) => vnode with props/emits set as static properties, inheritAttrs supported; typed with FunctionalComponent<Props, Emits, Slots>.compilerOptions.isCustomElement: tag => tag.includes('-') (build-time, in the Vite plugin options); force a native element to be treated as a component with is="vue:my-component"; pass complex data as DOM properties with the .prop modifier (:value.prop or .value).defineCustomElement(Comp) returns a class for customElements.define('my-el', …); shadowRoot: false (3.5) renders in the light DOM; props are reflected from attributes (kebab → camelCase, Number/Boolean cast) and exposed as DOM properties; events are dispatched as CustomEvent with detail; slots are native (no scoped slots); provide/inject works only between custom elements; configureApp (3.5) installs plugins into the internal app; .ce.vue SFCs inline their CSS into the shadow root; useHost()/useShadowRoot() (3.5) and this.$host give access to the host element.register() that defines all elements; ship types via GlobalComponents augmentation. The guide's comparison: web components lack a data-flow model, Vue components need Vue.:style to reactive values and let CSS transition interpolate), and watchers that tween numbers with a library such as GSAP for animated counters.A functional component with a scoped slot and a directive, in h():
import { h, withDirectives, type FunctionalComponent } from 'vue'
import { vFocus } from './directives'
interface Props { times: number }
type Slots = { default: (props: { index: number }) => any }
const Repeat: FunctionalComponent<Props, {}, Slots> = (props, { slots }) =>
h('ul', Array.from({ length: props.times }, (_, index) =>
withDirectives(h('li', { key: index }, slots.default?.({ index })), [[vFocus, index === 0]]),
))
Repeat.props = { times: { type: Number, required: true } }
export default Repeat
A Vue component shipped as a custom element:
import { defineCustomElement } from 'vue'
import Rating from './Rating.ce.vue' // .ce.vue inlines the styles into the shadow root
export const RatingElement = defineCustomElement(Rating, {
configureApp(app) { app.provide('theme', 'dark') }, // 3.5+
})
export function register() {
customElements.define('team-rating', RatingElement)
}
const p = h('p', 'hi'); return h('div', [p, p]) is invalid: the same vnode instance cannot appear twice. The guide says to use a factory (Array.from({ length: 2 }, () => h('p', 'hi'))). Templates never hit this because the compiler generates fresh vnodes.
v-if, v-for and static text in the SFC Playground and read the generated render function; find the patch flags, the hoisted nodes and the block calls.defineCustomElement, use it in a plain HTML page, and pass an object through a .prop binding from a Vue parent.onRenderTriggered counts."Because it knows the template's structure ahead of time. It hoists static nodes so they are created once, stringifies large static trees, attaches patch flags to dynamic nodes so the runtime checks only the bindings that can change, and flattens the tree into blocks that track only dynamic descendants, so a stable structure is skipped wholesale during a patch. Hydration uses the same information to skip static content. A hand-written h() tree has none of these hints, so every node is fully diffed; that is why the guide recommends templates unless the structure is genuinely dynamic."
Nothing yet. Notes filed here show up on this page and the section overview.
TypeScript with Vue
The TypeScript chapters — project setup and tsconfig, typing props, emits, refs, computed, event handlers, provide/inject and template refs with the Composition API, the Options API equivalents, and the utility types.
API reference map
The Vue API reference as a quiz sheet — application API and config, general helpers, every built-in directive with its modifiers, special elements and attributes, the SFC spec and script setup rules, reactivity utilities, compile-time flags and the SSR API.