Vue JS Composition API 2 — Questions and Answers
Question 1: What are composables in Vue 3?
- Reusable functions that encapsulate stateful logic using Composition API (Correct answer)
- Vue plugins that add global methods
- Components without a template
- Functions that replace Vuex stores
Correct answer: Reusable functions that encapsulate stateful logic using Composition API
Composables are functions (by convention prefixed with `use`) that use Composition API features like `ref`, `computed`, and lifecycle hooks to encapsulate and reuse logic.
Composables are functions that use Vue's Composition API to encapsulate reusable logic. Example: `function useMousePosition() { const x = ref(0); const y = ref(0); onMounted(() => { window.addEventListener('mousemove', e => { x.value = e.clientX; y.value = e.clientY }) }); return { x, y } }`. Each component that calls `useMousePosition()` gets its own reactive `x` and `y` state. Composables replaced Vue 2's mixins, solving name collision, unclear source, and implicit state problems. They can also be nested.
Question 2: In Vue 3, where must Composition API functions like `ref`, `computed`, and lifecycle hooks be called?
- Inside the `setup()` function or `<script setup>` — not inside conditionals or loops (Correct answer)
- Inside the component's `mounted` lifecycle hook
- Inside any function in the component
- Inside template expressions
Correct answer: Inside the `setup()` function or `<script setup>` — not inside conditionals or loops
Composition API functions must be called synchronously during component setup (not inside conditionals, loops, or async callbacks) so Vue can properly associate them with the component instance.
Vue's Composition API tracks which component instance is active during setup. Calling `ref`, `computed`, `onMounted` etc. outside of a synchronous setup context would either fail or associate with the wrong component. You cannot call them inside `if` statements, loops, or async functions (after an `await`). This is why composables should be called at the top of `setup()` or `<script setup>`. The pattern mirrors React's rules of hooks and ensures predictable hook ordering across renders.
Question 3: What does `toRefs` do in Vue 3?
- Converts a reactive object's properties into individual refs, preserving reactivity when destructured (Correct answer)
- Converts refs into a reactive object
- Removes reactivity from a reactive object
- Converts computed values to refs
Correct answer: Converts a reactive object's properties into individual refs, preserving reactivity when destructured
`toRefs` converts each property of a reactive object into a separate ref, so destructuring the result maintains reactivity.
If you have `const state = reactive({ count: 0, name: 'Vue' })`, destructuring directly — `const { count } = state` — breaks reactivity because `count` becomes a plain number. `const { count, name } = toRefs(state)` converts each property to a ref object, so `count.value` and `name.value` remain reactive. This is essential when returning reactive objects from composables: `return toRefs(state)` lets callers destructure without losing reactivity. `toRef(state, 'count')` creates a single ref for one property.
Question 4: What is the purpose of `shallowRef` in Vue 3?
- Creates a ref that only triggers reactivity for the .value assignment itself, not nested property changes (Correct answer)
- Creates a ref with a depth limit of 2
- Creates a non-reactive reference
- Creates a ref that only works with primitive values
Correct answer: Creates a ref that only triggers reactivity for the .value assignment itself, not nested property changes
`shallowRef` creates a ref where only replacing `.value` triggers updates. Mutations to nested properties of the value do NOT trigger reactivity, improving performance for large objects.
`ref(largeObject)` creates deep reactivity on the object's properties, which can be expensive. `shallowRef(largeObject)` only tracks the `.value` assignment: `shallowRef.value = newObject` triggers updates, but `shallowRef.value.nestedProp = 'change'` does not. This is useful for performance optimization when you always replace the entire value rather than mutating nested properties. Use `triggerRef(shallowRef)` to manually force an update after a nested mutation.
Question 5: How do you expose specific properties and methods to parent components in `<script setup>`?
- Using defineExpose({ property, method }) (Correct answer)
- Returning them from setup()
- All bindings in <script setup> are automatically exposed
- Using the expose option in defineOptions
Correct answer: Using defineExpose({ property, method })
`defineExpose` explicitly declares what a component with `<script setup>` exposes to parent components via template refs. By default, nothing is exposed.
In `<script setup>`, all bindings are private by default — unlike the Options API where everything on the component instance is accessible via `$refs`. To allow a parent to call a method or access a property via a template ref, the child must call `defineExpose({ myMethod, myData })`. The parent uses a ref: `<ChildComponent ref="child" />` and calls `child.value.myMethod()`. This encapsulation is intentional and avoids tight coupling between parent and child components.
Question 6: What is the `setup()` function's return value used for?
- To expose reactive data, computed properties, and methods to the template (Correct answer)
- To configure the component's lifecycle
- To register child components
- To define the component's emits
Correct answer: To expose reactive data, computed properties, and methods to the template
The object returned from `setup()` provides the bindings available in the component's template. Each property becomes accessible directly in the template.
The `setup()` function is the entry point of the Composition API in Options API components. Whatever is returned from `setup()` is merged with the component's template context. For example: `setup() { const count = ref(0); function increment() { count.value++ } return { count, increment } }` makes `count` and `increment` available in the template. Refs are auto-unwrapped in templates. In `<script setup>`, all top-level declarations are automatically returned, which is why no explicit return is needed.
What are composables in Vue 3?