Understanding Vue 3 Component System
A Vue component is a self-contained, reusable Vue instance that encapsulates its own template, data, methods, and lifecycle hooks. This modular approach is one of the framework's most powerful features.
Components extend HTML elements, allowing developers to encapsulate reusable code and decompose the user interface into independent, manageable pieces.
Key Characteristics and Benefits
- Reusability: Components can be utilized multiple times across an application, enhancing code reuse and maintainability.
- Encapsulation: Each component maintains its own scope, isolating its state and logic to prevent global namespace pollution.
- Composability: Complex interfaces can be built by assembling smaller, focused components.
- Reactivity: Component data is reactively bound to the view; the UI updates automatically when the underlying data changes.
- Modularity: Supports modular development practices and integrates with modern front-end build toolchains.
The component system enables developers to construct large-scale applications from a tree of small, reusable components. Every Vue application begins with a root component created via createApp, which is then mounted to a DOM element.
const RootComponent = { /* options */ }
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')
Global Component Registration
Components can be registered globallly, making them available through out the application.
const app = Vue.createApp({})
app.component('my-component-name', {
/* component options */
})
Once registered, the component can be used in templates:
<my-component-name></my-component-name>
Example: A Simple Global Component
<div id="app">
<simple-header></simple-header>
</div>
<script>
const app = Vue.createApp({})
app.component('simple-header', {
template: '<h1>A Custom Component!</h1>'
})
app.mount('#app')
</script>
Example: A Stateful Button Component
const app = Vue.createApp({})
app.component('click-counter', {
data() {
return {
clickCount: 0
}
},
template: `
<button @click="clickCount++">
Clicked {{ clickCount }} times!
</button>`
})
app.mount('#app')
Components can be reused multiple times:
<div id="app">
<click-counter></click-counter>
<click-counter></click-counter>
<click-counter></click-counter>
</div>
Local Component Registration
Global registration can lead to bloated bundle sizes, as unused components remain included. Local registration is preferable for better build optimization.
First, define components as plain JavaScript objects:
const LocalComponentA = {
/* ... */
}
const LocalComponentB = {
/* ... */
}
Then, register them locally within a parent component's components option:
const app = Vue.createApp({
components: {
'local-a': LocalComponentA,
'local-b': LocalComponentB
}
})
Example: Local Component
<div id="app">
<local-header></local-header>
</div>
<script>
const headerComponent = {
template: '<h1>Locally Registered Component!</h1>'
}
const app = Vue.createApp({
components: {
'local-header': headerComponent
}
})
app.mount('#app')
</script>
Single-File Components (.vue)
Single-file components (SFCs) provide a cleaner structure by colocating template, script, and style logic in one .vue file.
<!-- MyComponent.vue -->
<template>
<div>
<p>{{ greetingMessage }}</p>
<button @click="increaseCount">Increase</button>
</div>
</template>
<script>
export default {
data() {
return {
greetingMessage: 'Hello from Component',
count: 0
};
},
methods: {
increaseCount() {
this.count++;
}
}
}
</script>
<style scoped>
p {
color: blue;
}
</style>
Component Props
Prop are custom attributes used to pass data from a parent component to a child. The child must explicitly declare the props it expects.
Example: Basic Prop Usage
<div id="app">
<site-title title="Google"></site-title>
<site-title title="Runoob"></site-title>
<site-title title="Taobao"></site-title>
</div>
<script>
const app = Vue.createApp({})
app.component('site-title', {
props: ['title'],
template: `<h4>{{ title }}</h4>`
})
app.mount('#app')
</script>
Example: Dynamic Props with v-bind
Data flows reactively from parent to child. Changes in the parent's data propagate to the child component's props.
<div id="app">
<site-item
v-for="item in siteList"
:key="item.id"
:site-id="item.id"
:name="item.name"
></site-item>
</div>
<script>
const RootApp = {
data() {
return {
siteList: [
{ id: 1, name: 'Google' },
{ id: 2, name: 'Runoob' },
{ id: 3, name: 'Taobao' }
]
}
}
}
const app = Vue.createApp(RootApp)
app.component('site-item', {
props: ['siteId','name'],
template: `<h4>{{ siteId }} - {{ name }}</h4>`
})
app.mount('#app')
</script>
Prop Validation
For robust components, you can define validation rules for props by providing an object instead of an array.
app.component('validated-component', {
props: {
// Basic type check (null and undefined pass any type)
propNumber: Number,
// Multiple possible types
propMultiType: [String, Number],
// Required string
propRequiredString: {
type: String,
required: true
},
// Number with a default value
propNumberWithDefault: {
type: Number,
default: 100
},
// Object with a factory function for default value
propObjectWithDefault: {
type: Object,
default: function() {
return { message: 'default message' }
}
},
// Custom validator function
propCustomValidation: {
validator: function(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
}
}
})
If a prop fails validation during development, Vue will output a console warning. Valid type values can be native constructors like String, Number, Boolean, Array, Object, Date, Function, Symbol, or a custom constructor function.