Components are
are also Vue instances
accept (mostly) same options object
provide same life cycle hooks
may be:
extended HTML elements with encapsulated code
custom elements attached with behavior
appear as native HTML element extended with "is" attribute
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
make sure register BEFORE instantiate the root Vue element
Once registered, to use
<my-component></my-component>
use "components" option inside a Vue instance / component's option object
new Vue({ ... components: { 'my-component' : ...
Vue can only retrieve template after browser parse / normalize
HTML has restricting rules so for example <my-row> will be removed as invalid under <table>
such as <ul><ol><table><select>
workaround: <table><tr is="my-row"></tr>
this limitation does not apply when using string templates from
<script type="text/x-template">
JavaScript inline template string
.vue components
prefer using string templates whenever possible
most the same as Vue application constructor
except data:
must be a function
reason: data is shared by all instance of the components, must use function to encapsulate
correct:
data : function() { return {...}}
incorrect:
data : function() { return aGlobalData } // still the same object
components are mean to be composed together (parent child)
communications
pops down (parent -> child)
declare props in child's option object
props:['message'],
a prop can be used like a data,
also made available in childVm as "childVm.message"
use camelCase (myMessage) in prop declaration
pass down to child
<child message="hello!"></child>
ALWAYS pass down a string even "1"
<child v-bind:my-message="parentMsgData"></child> // bind to parent data
<child :my-message="parentMsgData></child> // shorthand
can pass down number
one way only
should not mutate the prop inside child (get warning in console), will cause refresh
use a local copy
if prop is an object or array, mutate the element inside WILL affect parent state
for HTML template only (string template not apply)
HTML attributes are case insensitive
use kabab-case equivalent in HTML attribute (my-message)
validation
can define type, default (value / function), validator
see https://vuejs.org/v2/guide/components.html
Non-prop attributes
even not declared, will pass down to root element of the component instance as attribute
if there's a conflict (pass down prop & root element attribute)
most attributes the passed down attribute will replace the component set attribute
"class" & "style" attribute will be merged
"v-on" custom events up (child -> parent)
Every Vue instance implements an event interface (see API)
Listen to a child Vue event
from either child or parent
parent:
<child-component v-on:childEvent="parentListener">
@childEvent = "..." //shorthand
Listen to a native event of child's root component
<child-component v-on:click.native="onClickHandler">
"two way binding a prop"
<child-component :foo.sync="bar">
expends to :foo="bar" @update:foo="val => bar=val">
for child to change parent state, needs to explicitly emit this.$emit('update:foo', newVal)
<child-component v-bind.sync="{foo: 1, bar: 2}"> ???
Input component
<input v-model="something"> is syntactic sugar for
<input
v-bind:value="something"
v-on:input="something = $event.target.value">
when using custom component, compiled to
<costom-input
:value = "something"
@input = "value => {something=value}">
inside custom component
props: ['value']
methods:{ someMethod:function(){.... this.$emit('input',Number(...))
Trigger an event
in child component's method
methods:{methodName:function(){... this.$emit('eventName', {...//eventobj })
Change name of prop & event
default: 'value' for pass down, 'input' for event
may conflict with such as checkbox
in Vue component option obj, model:{prop:"newPropName",event:"newEventName"}
Non-parent/child communication
use an empty Vue instance as a bus
Content distribution
wrap up child component inside parent component like
<parent><child>distributed content</child></parent>
modeled after the Web Component spec draft (at the time being) and use special <slot>
scope
if <child>{{something}}</child>
something - bound to parent's data // because this is written in parent template
rule:
everything in parent template compiled in parent scope
everything in child template compiled in child scope
distributed content (what is in parent template wrapped in child tag) compiled in parent scope
single slot in child
in child template, <slot> distributed content will place here or this default will be distributed </slot>
named slot
in child named & single slot all exists
<slot name="header"></slot>...<slot></slot>...<slot name="footer"></slot>
in parent, between opening/closing <child-element> tags (distributed content)
content of an element with attribute slot="header" goes into child slot header
so do footer, etc.
rest goes to single unnamed slot
scoped slot
https://vuejs.org/v2/guide/components.html#Scoped-Slots
Parent can pass an element (<template> for example) with slot-scope="props" into child
the distributed content as a template (not rendered) instead of as rendered static content
In child define <slot text="something"><slot>, and the value will fill {{props.text}} in passed down template
typical use case: use scopped slots for list
in child template
<ul><slot name="item" v-for="item in items" :text="item.text"></slot></ul>
in parent
<child-list :item="items"><li slot="item" slot-scope="props" class="someclass">{{props.text}}</li><child-list>
this renders child template, so comes ul
the the looped slot in ul to be filled with the distributed content as template
each loop "item" is given different value in items, text is given as item.text
in template given by parent as distributed content
specified slot="item" to match slot name in child
specified slot-scope so slot text becomes props.text
can use destructuring expression in slot-scope
without: slot-scope="props" & {{props.text}} in template
with: slot-scope = "{text}" & {{text}} in template
Dynamic components
use <component v-bind:is="someDynamicComponentInData"></component> to define a dynamic component which dynamically binds to someDynamicComponentInData
assign vm.someDynamicComponentInData to different component (global or locally registered) to change the binding
keep switched-out components in memory (preserve state, avoid re-rendering)
<keep-alive><component :is="..."></component></keey-alive>
include / exclude : string or RegExp or Array to match components
component's 'activated' 'deactivated' lifecycle hooks will be invoked
misc
for reusable components
api: props, events, slots
refer to component
in parent <child-component ref='childref'>
in code: parentvm.$refs.childref
avoid using in templates or computed properties, only populated after components rendered
async components
lazy load, see doc
component naming convention
kebab-case camelCase ParscalCase all works
camelCase ParscalCase only work in string templates
recursive components, circular references
see document
inline template
<child-component inline-template><div>this will be compiled as thecomponent's own template not parent's distributed content</div></child-component>
not recommended
x-template
define and use
<script type="text.x-template" id="my-template"><div>...</div></script>
Vue.component('my-component',{template:'#my-template'})
should avoid unless for small or demos
cheap static components v-once
template: '<div v-once>.....</div>'
only evaluated once and cached
https://vuejs.org/v2/guide/single-file-components.html
file with .vue extension work with build tools webpack or browserify
<template><div>....</div></template>
<script>module.exports={data:....
or <script src="./my-component.js"></script>
<style scoped>...// styles here
or <style src="./my-component.css"></style>
use vue-loader for webpack
See this example: https://codesandbox.io/s/o29j95wx9