Composing Components
Registering a Component
Vue.js allows you to treat registered ViewModel constructors as reusable components that are conceptually similar to Web Components, without requiring any polyfills. To register a component, first create a subclass constructor of Vue using Vue.extend()
, then use the global Vue.component()
method to register that constructor:
1 | // Extend Vue to get a reusable constructor |
To make things easier, you can also directly pass in the option object instead of an actual constructor:
1 | // Implicitly call Vue.extend, then register the returned constructor. |
Then you can use it in a parent ViewModel’s template (make sure the component is registered before you instantiate your top-level ViewModel):
1 | <div v-component="my-component"></div> |
If you prefer, components can also be used in the form of a custom element tag:
1 | <my-component></my-component> |
To avoid naming collisions with native elements and stay consistent with the W3C Custom Elements specification, the component’s ID must contain a hyphen -
to be usable as a custom tag.
It is important to understand the difference between Vue.extend()
and Vue.component()
. Since Vue
itself is a constructor, Vue.extend()
is a class inheritance method. Its task is to create a sub-class of Vue
and return the constructor. Vue.component()
, on the other hand, is an asset registration method similar to Vue.directive()
and Vue.filter()
. Its task is to associate a given constructor with a string ID so Vue.js can pick it up in templates. When directly passing in options to Vue.component()
, it calls Vue.extend()
under the hood.
Vue.js supports two different API paradigms: the class-based, imperative, Backbone style API, and the markup-based, declarative, Web Components style API. If you are confused, think about how you can create an image element with new Image()
, or with an <img>
tag. Each is useful in its own right and Vue.js tries to provide both for maximum flexibility.
Data Inheritance
Inheriting Objects from Parent as $data
A child component can inherit data from its parent’s data by combining v-component
with v-with
. When given a single keypath without an argument, the child Component will use that value as its $data
(hence that value must be an Object):
1 | <div id="demo-1"> |
1 | // registering the component first |
Result:
Inheriting Individual Properties with v-with
When v-with
is given an argument, it will create a property on the child Component’s $data
using the argument as the key. That property will be kept in sync with the bound value on the parent:
1 | <div id="parent"> |
1 | new Vue({ |
Result:
{{parentMsg}}
Using v-component
with v-repeat
For an Array of Objects, you can combine v-component
with v-repeat
. In this case, for each Object in the Array, a child ViewModel will be created using that Object as data, and the specified component as the constructor.
1 | <ul id="demo-2"> |
1 | var parent2 = new Vue({ |
Result:
Accessing Child Components
Sometimes you might need to access nested child components in JavaScript. To enable that you have to assign a reference ID to the child component using v-ref
. For example:
1 | <div id="parent"> |
1 | var parent = new Vue({ el: '#parent' }) |
When v-ref
is used together with v-repeat
, the value you get will be an Array containing the child components mirroring the data Array.
Event Communication Between Nested Components
Although you can directly access a ViewModels children and parent, it is more convenient to use the built-in event system for cross-component communication. It also makes your code less coupled and easier to maintain. Once a parent-child relationship is established, you can dispatch and trigger events using each ViewModel’s event instance methods.
1 | var Child = Vue.extend({ |
Encapsulating Private Assets
Sometimes a component needs to use assets such as directives, filters and its own child components, but might want to keep these assets encapsulated so the component itself can be reused elsewhere. You can do that using the private assets instantiation options. Private assets will only be accessible by the instances of the owner component and its child components.
1 | // All 5 types of assets |
Alternatively, you can add private assets to an existing Component constructor using a chaining API similar to the global asset registration methods:
1 | MyComponent |
Content Insertion Points
Single insertion point
When creating reusable components, we often need to access and reuse the original content in the hosting element, which are not part of the component. Vue.js implements a content insertion mechanism that is compatible with the current Web Components spec draft, using the special <content>
element to serve as insertion points for the original content. When there is only one <content>
tag with no attributes, the entire original content will be inserted at its position in the DOM and replaces it. Anything originally inside the <content>
tags is considered fallback content. Fallback content will only be displayed if the hosting element is empty and has no content to be inserted. For example:
Template for my-component
:
1 | <h1>This is my component!</h1> |
Parent markup that uses the component:
1 | <div v-component="my-component"> |
The rendered result will be:
1 | <div> |
Multiple insertion points and the select
attribute
<content>
elements have a special attribute, select
, which expects a CSS selector. You can have multiple <content>
insertion points with different select
attributes, and each of them will be replaced by the elements matching that selector from the original content.
Template for multi-insertion-component
:
1 | <content select="p:nth-child(3)"></content> |
Parent markup:
1 | <div v-component="multi-insertion-component"> |
The rendered result will be:
1 | <div> |
The content insertion mechanism provides fine control over how original content should be manipulated or displayed, making components extremely flexible and composable.
Next: Building Larger Apps.