Skip to content

Vue Framework

50 companion flashcards · AI-assisted study content · Open the deck →

This deck walks you through the core concepts of Vue.js, starting with the basics of what Vue is and how its reactivity system works under the hood. From there it moves into the practical building blocks you'll use every day: the Composition API versus the Options API, the script setup syntax, and the most important directives like v-if, v-for, v-bind, v-on, and v-model. You'll also get clear distinctions between commonly confused pairs, such as ref() versus reactive(), and v-if versus v-show, which are some of the most frequently tested ideas in Vue interviews.

It's a great fit if you're learning Vue for the first time, transitioning from Vue 2 to Vue 3, or reviewing Vue fundamentals before a technical interview. Developers coming from other frameworks like React or Angular will also find it useful as a quick way to map familiar concepts onto Vue's mental model. Even if you've been using Vue for a while, the focused questions on reactivity and directive behavior are a good way to make sure your understanding is solid and consistent.

Because these cards lean on terminology and subtle differences, active recall works especially well: try to answer in your own words before flipping the card, and pay extra attention to the "what's the difference between X and Y" prompts since they tend to reveal gaps. Space your reviews over several short sessions rather than cramming, and consider pairing the deck with a small Vue project so you can see each concept in action. Connecting the flashcards to real code will make the answers stick far longer than rote memorization alone.

Introduction to Vue and the Reactivity System

Vue.js is a progressive JavaScript framework designed primarily for building user interfaces. Its progressive nature means it can be adopted incrementally, allowing developers to use as much or as little of the framework as their project requires, and it focuses on the view layer so it can be integrated smoothly with other libraries or dropped into an existing codebase. The centerpiece of Vue's power is its reactivity system, which automatically tracks dependencies in your data and updates the DOM whenever reactive state changes. In Vue 2, this tracking was implemented with Object.defineProperty to intercept property access, but Vue 3 replaced that approach with Proxy objects, which provide a more complete and efficient way to intercept get and set operations on reactive data.

To create reactive state in Vue 3, the Composition API provides two primary functions: reactive() and ref(). The reactive() function takes an object and returns a deeply reactive proxy of the original, so you can read and write its properties directly, such as state.count. The ref() function, on the other hand, wraps a single value—either a primitive or an object—and requires the .value accessor when used inside a script. Refs are automatically unwrapped when used inside templates, so the .value is not needed there. The choice between them often comes down to ergonomics: ref() is more flexible because it can hold primitives like numbers and strings, while reactive() is convenient for grouping related state into an object that can be accessed without .value.

Both reactive() and ref() integrate seamlessly with the rest of the Vue reactivity system, so any component or computed property that reads from them will automatically re-render when their values change. This makes it easy to model state in whichever shape best fits the problem at hand, and to compose pieces of state across components without manually wiring up subscriptions or update notifications.

Component API Styles: Options vs Composition

Vue offers two main API styles for organizing component logic. The traditional Options API organizes code into predefined options such as data() for reactive state, methods for functions, computed for derived values, watch for side effects, and lifecycle hooks like mounted(). This approach is intuitive for newcomers because each concern has a designated place in the component definition, and it remains fully supported in Vue 3.

The Composition API, introduced and recommended in Vue 3, takes a different approach by exposing function-based APIs such as ref, reactive, computed, watch, and lifecycle hooks that can be composed freely. This style is used inside a setup() function or, more commonly, inside the script setup block. The advantage of the Composition API is that it lets you group related logic by feature rather than by option type, which makes complex components easier to read and reason about, and it pairs naturally with TypeScript. Both styles are fully supported, and you can even mix them within the same project depending on the needs of each component.

The script setup syntax is a compile-time syntactic sugar layered on top of the Composition API. Inside this block, every top-level binding—including variables, functions, and imports—is automatically exposed to the template without needing to be returned from a setup() function. This dramatically reduces boilerplate, especially for component props and emits which are declared through the compiler macros defineProps and defineEmits. As a result, script setup has become the default and most ergonomic way to write Vue 3 components.

Template Directives

Vue's template directives are special attributes that add reactive behavior to the rendered DOM. The v-if directive conditionally renders an element, completely destroying and recreating it in the DOM whenever the condition toggles, and it can be paired with v-else-if and v-else for multiple branches. By contrast, v-show always keeps the element in the DOM and simply toggles its CSS display property. The trade-off is that v-if has a higher toggle cost but a lower initial render cost, while v-show is the opposite. As a rule of thumb, use v-show for elements that toggle frequently, and v-if for conditions that rarely change or depend on expensive initialization.

The v-for directive renders lists by iterating over an array or object, and it requires a unique :key attribute on each item so that Vue can efficiently patch and reorder DOM elements. Without keys, Vue falls back to an in-place patch strategy that can cause subtle bugs with stateful elements. The v-bind directive dynamically binds an HTML attribute or a component prop to an expression, and it has a shorthand of just a colon, as in :src="imageUrl". You can also bind multiple attributes at once by passing an object to v-bind.

For two-way data binding between form inputs and reactive state, Vue provides v-model. It is essentially syntactic sugar for combining :value with an @input listener that updates the bound value. Modifiers like .lazy, .number, and .trim adapt this behavior—for instance, .lazy syncs on the change event instead of every keystroke, and .trim removes leading and trailing whitespace. Event listening itself is handled by v-on, which has a shorthand of @, and which supports modifiers such as .prevent, .stop, .once, .self, and key modifiers like .enter for keyboard events.

Computed Properties and Watchers

Computed properties are cached reactive derivations that automatically track the reactive values they read. When any of those dependencies change, the computed value is invalidated and recomputed on next access; otherwise, the cached result is returned. In the Composition API, you create one with the computed() function, and in the Options API you define them inside the computed option. Computed properties should remain pure and side-effect free, since they are evaluated lazily and may re-run at unexpected times during rendering.

The key distinction between computed properties and methods is caching. A method runs on every re-render whenever it is called from the template, while a computed property only re-evaluates when its dependencies change. This makes computed properties ideal for expensive derivations based on reactive state, while methods are better suited to event handlers and actions that intentionally produce side effects. For cases where you need to write to a computed value, Vue also supports writable computed properties by providing both a get and a set function in the object form of computed().

Watchers are the appropriate tool when you need to perform side effects in response to state changes. The Composition API provides watch() and watchEffect(). The watch() function requires you to explicitly specify the reactive sources to observe and gives you access to both the new and previous values. The watchEffect() function is more automatic: it tracks every reactive dependency accessed inside its callback and runs immediately on creation. In the Options API, watchers are defined inside the watch option, providing a similar capability with a different syntax.

Component Lifecycle, Props, and Emits

Every Vue component goes through a series of lifecycle stages, and Vue exposes hooks that fire at key moments. In the Composition API these are functions like onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, and onUnmounted. The onMounted hook fires after the component has been added to the DOM, making it the right place for tasks that require access to the rendered DOM—such as fetching initial data, integrating with third-party libraries like charts or maps, attaching event listeners, or focusing an input via a template ref. Conversely, onUnmounted runs after the component has been removed, and it is the appropriate place to clean up those listeners, clear timers, cancel in-flight requests, or close WebSocket connections to prevent memory leaks.

Component communication begins with props, which are declared in script setup using the defineProps compiler macro. defineProps accepts a schema describing each prop's type, whether it is required, and a default value if optional. On the opposite side of the data flow, child components communicate upward to their parents by emitting events. In script setup, you obtain an emitter with defineEmits(['eventName']) and then call emit('eventName', payload). The parent listens with @eventName="handler". Emits can also be declared with an object syntax that includes a validation function, and a false return value triggers a development-mode warning to help catch mistakes.

Beyond props and events, script setup components are closed by default, meaning parents cannot reach into a child to read its internal state. To selectively expose bindings, the child uses the defineExpose macro to explicitly publish properties or methods. On the parent side, a template ref can be declared with ref(null) and attached to a child component via ref="myRef", giving access to whatever the child has exposed. This pattern is essential for building imperative component APIs, such as exposing a reset or validate method from a form component.

Slots, Provide/Inject, and Advanced Features

Slots are how a parent component passes template content into designated areas of a child component. The child simply renders slot, and any content placed between the child's opening and closing tags is rendered there. When a component needs multiple insertion points, named slots allow the parent to target specific outlets using slot name="header" in the child and template #header in the parent. The # symbol is shorthand for v-slot:, and any unnamed content falls into the default slot. Scoped slots go further by letting the child pass data back to the parent's slot content, enabling the parent to customize rendering while the child owns the underlying data and iteration logic.

When data needs to flow through many layers of components, props become cumbersome—a problem often called prop drilling. Vue's provide and inject APIs solve this by allowing an ancestor to provide a value that any descendant can inject, regardless of how deeply nested it is. In the Composition API, the ancestor calls provide('theme', ref('dark')) and any descendant calls const theme = inject('theme'). Reactive values remain reactive through this channel because the ref itself is what is shared. Provide and inject are best reserved for app-wide concerns like theme, locale, or authentication, while complex shared state is generally better managed by Pinia.

Vue 3 also includes several built-in components and helpers that address more specialized needs. The Teleport component renders its content in a different DOM location than where it is logically placed, which is invaluable for modals, tooltips, and notifications that need to escape parent CSS constraints such as overflow: hidden. The Suspense component coordinates async dependencies by rendering a fallback slot while waiting for async components or an async setup() to resolve. Async components themselves, created with defineAsyncComponent, load their code on demand and integrate with route-based code splitting. Custom directives, defined as objects with lifecycle hooks like mounted, give low-level access to the DOM for reusable behavior such as auto-focus. Finally, helpers like toRefs() preserve reactivity when destructuring a reactive object, and shallowRef() creates a ref that is only reactive at the top level—useful as a performance optimization when the inner structure is large or managed externally.

State Management with Pinia

For applications whose state spans many components, Vue's official state management library is Pinia, which replaced Vuex as the recommended solution. Pinia is intentionally simple: there are no separate mutations, every store supports full TypeScript inference, and stores can be written in either the Options style or the Composition style. Each store is independent and modular, and Pinia integrates with the Vue DevTools for time-travel debugging and state inspection.

A store is defined with defineStore() from the pinia package. In the Options style, you supply an object with a state function returning the initial data, a getters object for derived values, and an actions object for methods that contain business logic or async work. In the Composition style, the second argument is a setup function that uses ref, computed, and regular functions and returns whatever should be exposed. This dual syntax means you can pick the style that best matches the rest of your component code, and stores can even be split across multiple files when they grow large.

Pinia getters are equivalent to computed properties for stores: they are cached and only recalculate when their reactive dependencies change. They can also reference other getters, including via this in the Options style. Pinia actions, unlike the old Vuex mutations, freely support async operations, can call other actions, and are not strictly required to be synchronous. Once a store is defined, you consume it in any component by calling the returned useStore() function, after which the store's state, getters, and actions are available as direct properties on the result.

Routing with Vue Router

Vue Router is the official routing library for Vue.js, and it enables single-page application navigation by mapping URL paths to components. It supports nested routes for hierarchical layouts, dynamic route matching with parameters, named routes and views, and navigation guards for controlling access. The library can operate in HTML5 history mode, which produces clean URLs, or in hash mode, which is easier to deploy to static hosts that lack server-side rewrite support.

Routes are defined as an array of objects, each specifying a path and a component. Dynamic segments are introduced with a colon, so a path like /user/:id matches URLs such as /user/42 and exposes the value as route.params.id. A catch-all route such as /:pathMatch(.*)* is typically used to render a 404 page for unmatched URLs. The router instance is created with createRouter({ history: createWebHistory(), routes }) and then passed to the Vue application via app.use(router).

Programmatic navigation is performed through methods on the router instance. Calling router.push('/about') navigates to a new URL while adding an entry to the browser history, router.replace('/login') does the same without leaving a history trace, and router.go(-1) moves backward or forward through the history stack. Named routes can be targeted by passing an object like { name: 'user', params: { id: 1 } }. Navigation guards add control over when these transitions occur: beforeEach runs globally before every navigation, beforeEnter is scoped to a specific route, and beforeRouteEnter and beforeRouteLeave are defined inside components. They are commonly used for authentication checks, fetching data before a route is entered, or preventing navigation away from a page with unsaved changes.

Frequently asked questions

What is Vue.js?

Vue.js is a progressive JavaScript framework for building user interfaces. It is designed to be incrementally adoptable and focuses on the view layer, making it easy to integrate with other libraries or existing projects.

What is the Composition API in Vue 3?

The Composition API is a set of function-based APIs (ref, reactive, computed, watch, lifecycle hooks) that allow organizing component logic by feature rather than by option type. It is used inside setup() or <script setup>.

What is the difference between v-if and v-show?

v-if completely adds/removes the element from the DOM (higher toggle cost). v-show always keeps the element in the DOM and toggles its CSS display property (higher initial render cost).
Use v-show for frequent toggling, v-if for conditions that rarely change.

What are v-model modifiers?

Vue provides built-in modifiers for v-model:
  • .lazy — syncs on change instead of input
  • .number — casts input to a number
  • .trim — trims whitespace from input
Example: <input v-model.trim="name">

Can computed properties have setters?

Yes. A writable computed can define both a getter and setter:
const fullName = computed({
  get: () => first.value + ' ' + last.value,
  set: (val) => { [first.value, last.value] = val.split(' ') }
})

When should you use the onMounted hook?

Use onMounted when you need access to the rendered DOM, such as:
  • Fetching data from an API
  • Initializing third-party libraries (charts, maps)
  • Setting up event listeners on DOM elements
  • Accessing template refs

What are scoped slots?

Scoped slots let a child component pass data back to the parent's slot content:
Child: <slot :item="item"></slot>
Parent: <template #default="{ item }">{{ item.name }}</template>
This enables the parent to customize rendering while the child provides the data.

What are Pinia actions?

Actions in Pinia are methods that can contain business logic and async operations. Unlike Vuex, there are no separate mutations:
actions: {
  async fetchUser(id) {
    this.user = await api.getUser(id)
  }
}

What are navigation guards in Vue Router?

Navigation guards are hooks that run before or after route changes:
  • beforeEach — global, runs before every navigation
  • beforeEnter — per-route guard
  • beforeRouteEnter / beforeRouteLeave — in-component guards
Used for authentication checks, data fetching, and access control.

What is provide/inject in Vue?

provide and inject enable dependency injection across the component tree without prop drilling:
Parent: provide('theme', ref('dark'))
Descendant: const theme = inject('theme')
Works across any depth of nesting, not just direct parent-child.

Drill this topic

50 flashcards on Vue Framework — free, no signup needed to start.

Study Vue Framework flashcards

LearnWiki pages are generated with AI assistance from LearnCoachAssist's reviewed study catalog and may contain errors — verify anything critical against your course materials.