Chapter 57: Vue Component Instance
The Vue Component Instance
This is not the same as the component definition (the .vue file or object you write), nor is it the same as the HTML tag you use in templates.
A component instance is a live, running object that Vue creates in memory when it actually renders your component on the screen.
Think of it like this:
- You write a blueprint → the .vue file or defineComponent({ … })
- Vue reads the blueprint and builds a real house → the component instance
- The instance is the living thing that has state, methods, DOM elements, event listeners, reactivity watchers, lifecycle status, etc.
Every time you see your component appear on the page (via <MyComponent />, v-for, router-view, dynamic :is, etc.), Vue creates one instance of it.
Why You Need to Understand Component Instances
Because almost everything you do with refs, events, lifecycle, provide/inject, $emit, $refs, $parent, $root, $slots, $attrs… is talking to the instance, not the blueprint.
Concrete Example – One Definition, Multiple Instances
Child component definition (blueprint)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!-- src/components/CounterButton.vue --> <template> <button @click="increment"> Count: {{ count }} </button> </template> <script setup> import { ref } from 'vue' const count = ref(0) function increment() { count.value++ } </script> |
Parent – creating multiple instances
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<template> <div> <h2>Three separate counters</h2> <!-- Each <CounterButton> is a separate, independent instance --> <CounterButton /> <CounterButton /> <CounterButton /> </div> </template> <script setup> import CounterButton from '@/components/CounterButton.vue' </script> |
What happens in memory
Vue creates three separate instances of CounterButton:
- Instance A: has its own count = 0
- Instance B: has its own count = 0
- Instance C: has its own count = 0
Click button A → only A’s count becomes 1 Click button B → only B’s count becomes 1
Each instance:
- has its own reactive state (own refs)
- has its own DOM subtree (own <button>)
- has its own event listeners
- has its own lifecycle (mounts, updates, unmounts independently)
- can be individually destroyed when parent removes it
How to Get a Reference to an Instance (Template Refs)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<CounterButton ref="btnA" /> <CounterButton ref="btnB" /> <script setup> import { ref } from 'vue' const btnA = ref(null) const btnB = ref(null) // btnA.value is the actual component instance object // You can access exposed properties/methods onMounted(() => { console.log(btnA.value) // → the live CounterButton instance btnA.value.increment?.() // if child exposes increment() }) </script> |
Child exposes something (optional but powerful)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<script setup> import { ref, defineExpose } from 'vue' const count = ref(0) function increment() { count.value++ } defineExpose({ count, // expose the ref itself increment, // expose method reset: () => { count.value = 0 } }) </script> |
Now parent can do:
|
0 1 2 3 4 5 6 7 |
btnA.value.reset() btnA.value.count.value++ // direct access (not always recommended) |
Key Properties & Methods Every Instance Has
When you get a component instance via ref.value, you can access:
| Property / Method | What it is | Common use case |
|---|---|---|
| $el | The root DOM element of this component | Get real DOM node |
| $refs | Object of template refs inside this component | Access child DOM / instances |
| $parent / $root | Parent / root component instance | Traverse up (avoid if possible) |
| $slots | Object containing slots passed by parent | Check if slot exists |
| $attrs | All attributes passed that are not props | Forward to child elements |
| $emit (Options API) | Emit custom event to parent | Old style |
| emit() (Composition) | Modern emit | @custom-event |
| props | The props object | Read incoming data |
| exposed | What child defineExpose() made public | Parent calls child methods |
Lifecycle of a Component Instance
- Created — instance is born, reactive system is set up
- Mounted — rendered to DOM, onMounted runs
- Updated — data changed → re-rendered, onUpdated runs
- Unmounted — removed from DOM, onUnmounted runs, instance is destroyed
Each instance goes through this cycle independently.
Quick Summary Table – Component Instance Facts
| Question | Answer |
|---|---|
| How many instances per use? | One new instance every time the component is rendered |
| Are instances independent? | Yes — each has own state, refs, DOM, lifecycle |
| How to get an instance? | Template ref → ref=”myComp” → myComp.value |
| Can parent call child methods? | Yes — if child uses defineExpose({ myMethod }) |
| Can child access parent? | Yes — via getCurrentInstance().parent (rarely needed) |
| Destroyed when? | When removed from DOM (v-if=false, v-for removes it, route change…) |
| Memory? | Kept alive if inside <KeepAlive>, otherwise garbage collected |
Your Mini Practice Task
- Create CounterButton.vue with internal count + defineExpose({ getCount, reset })
- In parent → render three <CounterButton ref=”btns” />
- Add buttons: “Reset All”, “Log All Counts”
- Use btns.value (array of instances) to call reset() on all or read getCount()
Any part confusing? Want full examples for:
- Template refs + <Transition> for animated focus?
- Accessing child component methods with TypeScript?
- Common bug with v-if + ref?
- Real Chart.js or Google Maps integration using refs?
Just tell me — we’ll build the next practical instance example together 🚀
