component object definition
A Vue component is an object where all properties are optional, but either a template or render function is required for the component to render. For example, an object with a render() method returning 'Hello world!' is a valid component. Most Vue applications use Single-File Components (.vue files), which the SFC compiler converts into an object exported as the default export.
component options definition
The properties of a component object are usually referred to as options. This is where the Options API gets its name. The options for a component define how instances of that component should be created. Components are conceptually similar to classes, though Vue doesn't use actual JavaScript classes to define them.
Props and events preferred over this.$parent and prop mutation
Props and events should be preferred for parent-child component communication, instead of using this.$parent or mutating props directly. An ideal Vue application follows "props down, events up". While this.$parent and prop mutation exist for edge cases where components are deeply coupled, they should be avoided in simple cases to maintain code simplicity and state flow clarity. Mutating props traded short-term convenience for long-term maintainability problems.
Implicit parent-child communication: bad example (Options API)
Bad pattern in Options API - mutating props and using this.$parent:
```js
app.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: '<input v-model="todo.text">'
})
```
```js
app.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
methods: {
removeTodo() {
this.$parent.todos = this.$parent.todos.filter(
(todo) => todo.id !== vm.todo.id
)
}
},
template: `
<span>
{{ todo.text }}
<button @click="removeTodo">
×
</button>
</span>
`
})
```
Implicit parent-child communication: good example (Options API)
Good pattern in Options API - use emits for child-to-parent communication:
```js
app.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
emits: ['input'],
template: `
<input
:value="todo.text"
@input="$emit('input', $event.target.value)"
>
`
})
```
```js
app.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
emits: ['delete'],
template: `
<span>
{{ todo.text }}
<button @click="$emit('delete')">
×
</button>
</span>
`
})
```
Implicit parent-child communication: bad example (Composition API)
Bad pattern in Composition API - mutating props from child component:
```vue
<script setup>
defineProps({
todo: {
type: Object,
required: true
}
})
</script>
<template>
<input v-model="todo.text" />
</template>
```
```vue
<script setup>
const props = defineProps({
todo: {
type: Object,
required: true
}
})
function renameTodo() {
// Mutates the parent's reactive object via the prop
// In other words, the child is reaching into and changing parent-owned state.
props.todo.text = 'renamed by child'
}
</script>
<template>
<span>
{{ todo.text }}
<button @click="renameTodo">rename</button>
</span>
</template>
```
Implicit parent-child communication: good example (Composition API)
Good pattern in Composition API - use defineEmits for child-to-parent communication:
```vue
<script setup>
defineProps({
todo: {
type: Object,
required: true
}
})
const emit = defineEmits(['input'])
</script>
<template>
<input :value="todo.text" @input="emit('input', $event.target.value)" />
</template>
```
```vue
<script setup>
const props = defineProps({
todo: {
type: Object,
required: true
}
})
const emit = defineEmits(['update:todo'])
function renameTodo() {
// Emit a new object — the parent owns the update.
emit('update:todo', { ...props.todo, text: 'renamed by parent' })
}
</script>
<template>
<span>
{{ todo.text }}
<button @click="renameTodo">rename</button>
</span>
</template>
```
defineExpose for exposing properties in script setup
Components using <script setup> are closed by default - the public instance retrieved via template refs or $parent chains will not expose any bindings declared inside <script setup>. Use defineExpose compiler macro to explicitly expose properties. Refs are automatically unwrapped in the exposed shape.
Recursive components in script setup
An SFC can implicitly refer to itself via its filename. For example, a file named FooBar.vue can refer to itself as <FooBar/> in its template. This has lower priority than imported components. If a named import conflicts with the component's inferred name, alias the import.
Namespaced components in script setup
Component tags with dots like <Foo.Bar> can refer to components nested under object properties. This is useful when importing multiple components from a single file using import * as Form syntax.
$slots property and type
$slots is an object representing the slots passed by the parent component. Type: { [name: string]: Slot } where Slot = (...args: any[]) => VNode[]. Each slot is exposed on this.$slots as a function that returns an array of vnodes under the key corresponding to that slot's name. The default slot is exposed as this.$slots.default. If a slot is a scoped slot, arguments passed to the slot functions are available to the slot as its slot props. This property is readonly.
$el property and lifecycle
$el is the root DOM node that the component instance is managing. Type: any. $el will be undefined until the component is mounted. For components with a single root element, $el will point to that element. For components with text root, $el will point to the text node. For components with multiple root nodes, $el will be the placeholder DOM node that Vue uses to keep track of the component's position in the DOM (a text node, or a comment node in SSR hydration mode).
$parent property
$parent is the parent instance of the current component instance. Type: ComponentPublicInstance | null. It will be null for the root instance itself. This property is readonly.
$root property
$root is the root component instance of the current component tree. Type: ComponentPublicInstance. If the current instance has no parents, this value will be itself. This property is readonly.