An Introduction to Vue Components
Components are essentially custom HTML elements. They allow you to break your app down into understandable and reusable chunks, rather than having a single monolithic app. In this tutorial, you'll learn:
- How to create a component
- How to manage internal state with a component
- How to pass data into a component via props
- How to pass data from a component to its parent via
$emit()
Creating a Component
To create a component, you should call the Vue.component()
function. The Vue.component()
function takes 2 parameters: a unique string id
for the component, and the object definition
of the component.
Suppose you have a component whose id is hello
. Whenever you include an element <hello></hello>
in a Vue template, Vue will replace the element with your component's template. Below is an example of a component hello
with a template that displays a message in an <h1>
tag.
const helloComponent = Vue.component('hello', {
template: '<h1>Hello, World</h1>'
});
// Technically, a component is a function
typeof helloComponent; // 'function'
helloComponent.name; // 'VueComponent'
// Internally, Vue keeps a map from ids to components in
// `Vue.options.components`
Vue.options.components['hello'] === helloComponent; // true
// Renders "<h1>Hello, World</h1>"
const app = new Vue({
template: '<hello></hello>'
});
app.$mount('#content');
Internal State With Components
One advantage Vue has over React is two way data binding on form elements. Forms in Vue are trivial using v-model
, but they require a bit more work with React.
Suppose you wanted to extend the hello
component with an input, so the user can enter their name. You should add a data
function to your component definition
that returns the initial state of the component. Make sure you define an initial state for all the properties you want Vue to watch, even if it is null
.
Vue.component('hello', {
data: () => ({
name: 'World'
}),
template: `
<div>
<div>
<input v-model="name"></input>
</div>
<h1>Hello, {{name}}</h1>
</div>
`
});
// Displays "Hello, World" initially, changes based on input
const app = new Vue({
template: '<hello></hello>'
});
app.$mount('#content');
Here's how the component looks in action. You can also see a live example here.
Component Props
Suppose that, instead of having one component that handles both user input and displaying data, you want to have separate components. The top-level app
template will display the <input>
, and the hello
component will be responsible for displaying the value of the <input>
.
The way to pass data to the hello
component is using props. The v-bind:name="name"
binds the value of name
in the hello
component's template to the value of name
in the top-level app state.
// `props` is an array of prop names this component accepts. If you
// don't explicitly list a prop in `props`, you won't be able to use
// it in your template.
Vue.component('hello', {
props: ['name'],
template: '<h1>Hello, {{name}}</h1>'
});
// The app tracks `name` as internal state, and there's an input to
// modify `name` using `v-model`. Then, `v-bind:name` passes `name` as
// a prop to the `hello` component.
const app = new Vue({
data: () => ({ name: 'World' }),
template: `
<div>
<div>
<input v-model="name"></input>
</div>
<hello v-bind:name="name"></hello>
</div>
`
});
Here's a live example of the props-based hello
component.
$emit()
Props let you pass data into a component from a parent component. The $emit()
function lets you pass data from a component back to its parent, usually in response to an event.
Suppose you wanted to define a separate input-name
component that allowed the user to input their name. When the user clicks the 'Update' button, your app updates the user's name and updates the <h1>
tag. Here's how this works in Vue:
Vue.component('input-name', {
data: () => ({ name: 'World' }),
// When you click the "Update" button, Vue will emit an event `update`
// to the parent, with the current state of 'name'.
template: `
<div>
<input type="text" v-model="name">
<button v-on:click="$emit('update', name)">
Update
</button>
</div>
`
});
const app = new Vue({
data: () => ({ name: 'World' }),
// To listen to the 'update' event, you create the `input-name`
// component with a `v-on:update` attribute. `$event` contains
// the value of the 2nd parameter to `$emit()`.
template: `
<div>
<div>
<input-name v-on:update="setName($event)"></input-name>
</div>
<h1>Hello, {{name}}</h1>
</div>
`,
methods: {
// Define a method that Vue will call to handle the 'update' event.
setName: function(v) {
this.name = v;
}
}
});
app.$mount('#content');