1.使用組件
//組件命名規(guī)則:全小寫-分隔(kebab-case)
//注冊一個全局組件
Vue.component('my-component', {});
//注冊一個局部組件
new Vue({
components:{
'my-component':{}
}
});
<table>
<!-- ul,ol,table,select標簽限制了被包裹元素的類型,如果使用自定義組件會有問題 -->
<!-- my-row></my-row -->
<!-- 變通方案使用is屬性 -->
<tr is="my-row"></tr>
</table>
為什么component的data被設計為一個函數(shù)?
嘻嘻...你猜...
2.父子組件通信
父組件通過props向下傳遞數(shù)據(jù)給子組件,子組件通過events給父組件發(fā)送消息
組件實例作用域是孤立的,不應該在子組件內(nèi)部直接引用父組件數(shù)據(jù),應該通過props選項訪問
<!-- myMessage屬性camelCase在html中要寫成kebab-case -->
<!-- my-message綁定靜態(tài)字符串 -->
<my-component my-message="hello"></my-component>
<!-- :my-message動態(tài)綁定父組件變量,當父組件數(shù)據(jù)變化,會傳導給子組件 -->
<my-component :my-message="message"></my-component>
Vue.component('my-component', {
props:['myMessage'],
template:'<span>{{myMessage}}</span>'
});
var app = new Vue({
el: "#app",
data:{
message:"from parent"
}
});
props是單向綁定,父組件屬性變化會傳到給子組件,但不會反過來,不應該在子組件內(nèi)部直接改變prop值
<my-component :init="init" :size="size"></my-component>
Vue.component('my-component', {
props: ['init', 'size'],
template: '<span>{{myInit}}{{mySize}}</span>',
//組件內(nèi)部改變prop的2種方案:
//1.prop作為值初始值傳入,賦值給組件局部變量
data: function () {
return {myInit: this.init}
},
//2.prop作為初始值傳入,由組件處理為其他數(shù)據(jù)輸出
computed: {
mySize: function () {
return this.size.toLowerCase();
}
}
});
var app = new Vue({
el: "#app",
data: {
init: 0,
size: "six"
}
});
props如果是引用類型,在子組件內(nèi)部改變其屬性會影響父組件
可為組件props指定驗證規(guī)則
<my-component :pro-a="proA" :pro-b="proB" :pro-c="proC" :pro-d="proD"></my-component>
Vue.component('my-component', {
props: {
//String,Number,Boolean,Function,Object,Array,Symbol,null(任意類型)
proA: Number,
//多種類型
proB: [String, Number],
proC: {
type: String,
//設置默認值
default: "66",
//設置必填驗證
required: true
},
proD: {
type: Number,
//自定義默認值
default: function () {
return 6;
},
//自定義驗證規(guī)則
validator: function (value) {
return value >= 6;
}
}
},
template: '<span></span>'
});
var app = new Vue({
el: "#app",
data: {
//報錯
proA: "6",
//報錯
proB: [],
//報錯
proC: null,
//報錯
proD: 5
}
});
3.自定義事件
子組件通過自定義事件系統(tǒng)于父組件通信
- $on(eventName)監(jiān)聽事件
- $emit(eventName)觸發(fā)事件
<!-- 父組件可直接使用v-on監(jiān)聽子組件觸發(fā)的自定義事件 -->
<my-button @myevent="addTotal"></my-button>
<!-- 父組件監(jiān)聽子組件根元素上的原生事件,使用.native修飾符 -->
<my-button @click.native="addTotal"></my-button>
Vue.component('my-button', {
template: '<button @click="addCount">{{count}}</button>',
data: function () {
return {
count: 0
}
},
methods: {
addCount: function () {
this.count++;
//觸發(fā)自定義事件
this.$emit('myevent');
}
}
});
var app = new Vue({
el: "#app",
data: {
total: 0
},
methods: {
addTotal: function () {
this.total++;
}
}
});
v-model語法糖實際會轉(zhuǎn)化為::value="value" @input="value=$event.target.value",因此,自定義事件可以用來創(chuàng)建自定義表單輸入組件,使用v-model實現(xiàn)數(shù)據(jù)雙向綁定
{{currency}}
<!--my-currency :value="currency" @input="currency = $event.target.value"></my-currency-->
<my-currency v-model="currency"></my-currency>
Vue.component('my-currency', {
props: ["value"],
template: '<div><input ref="myInput" :value="value" @input="updateValue"/></div>',
methods: {
updateValue: function (event) {
var currency = "$" + event.target.value.replace(/\$/g, "");
this.$refs.myInput.value = currency;
//觸發(fā)input事件
this.$emit('input', currency);
}
}
});
var app = new Vue({
el: "#app",
data: {
currency: "0"
}
});
可定制組件的v-model的prop和event,避免沖突
{{currency}}
<!--my-currency :myvalue="currency" @myinput="currency = $event.target.value"></my-currency-->
<my-currency v-model="currency" value="hello"></my-currency>
Vue.component('my-currency', {
//可定制v-model
model: {
prop: 'myvalue',
event: 'myinput'
},
//釋放value屬性
props: ['myvalue', 'value'],
template: '<div><input ref="myInput" :value="myvalue" @input="updateValue"/></div>',
methods: {
updateValue: function (event) {
var currency = "$" + event.target.value.replace(/\$/g, "");
this.$refs.myInput.value = currency;
//觸發(fā)myinput事件
this.$emit('myinput', currency);
}
}
});
var app = new Vue({
el: "#app",
data: {
currency: "0"
}
});
非父子組件通信,簡單場景使用一個空Vue實例做事件中轉(zhuǎn),復雜情況請考慮狀態(tài)管理模式
var bus = new Vue();
bus.$emit('my-event', 6);
bus.$on('my-event', function(arg){});
4.使用Slots分發(fā)內(nèi)容
slots標簽讓組件內(nèi)容可以混合嵌套,類似Angular的transclusion
<!-- 最終生成html -->
<!--div>
<h1>child</h1>
<div>parent content</div>
</div-->
<my-component>
<div>parent content</div>
</my-component>
Vue.component('my-component', {
template: '<div><h1>child</h1><slot></slot></div>'
});
為slot指定name來配置如何分發(fā)內(nèi)容
<!-- 最終生成html -->
<!--div>
<h1>header</h1>
<p>footer</p>
</div-->
<my-layout>
<h1 slot="header">header</h1>
<p slot="footer">footer</p>
</my-layout>
Vue.component('my-layout', {
template: '<div><slot name="header"></slot><slot name="footer"></slot></div>'
});
作用域插槽,暴露數(shù)據(jù),允許父組件自定義渲染
<my-list :items="items">
<!-- template表示作用域插槽模板,scope為一臨時變量,來至子組件 -->
<template slot="listItem" scope="props">
<!-- 自定義渲染 -->
<li>{{props.text}}</li>
</template>
</my-list>
Vue.component('my-list', {
props: ['items'],
template: '<ul><slot name="listItem" v-for="item in items" :text="item"></slot></ul>'
});
var app = new Vue({
el: "#app",
data: {
items: ["1", "2"]
}
});
5.動態(tài)組件
通過<component>元素,動態(tài)綁定is特性,可使用同一個掛載點,動態(tài)切換組件
<component :is="currentView"></component>
<!-- keep-alive保留切換出去的組件在內(nèi)存,避免重新渲染 -->
<keep-alive>
<component :is="currentView"></component>
</keep-alive>
<button @click="change">change</button>
var myHeader = {
template: '<h1>Header</h1>'
};
var myFooter = {
template: '<div>Footer</div>'
};
var myText = {
template: '<input type="text"/>'
};
var app = new Vue({
el: "#app",
data: {
//currentView: myHeader
currentView: 'myFooter'
},
components: {
myFooter: myFooter,
myText: myText
},
methods: {
change: function () {
this.currentView = (this.currentView == 'myText' ? 'myFooter' : 'myText');
}
}
});
6.雜項
組件分3部分:
- props允許外部傳遞數(shù)據(jù)給組件
- events允許外部和組件內(nèi)部通信
- slots允許外部自定義內(nèi)容渲染到組件中
子組件索引
<!-- ref為子組件指定索引,用來在javascript中直接訪問子組件 -->
<my-component ref="my"></my-component>
Vue.component('my-component', {
data: function () {
return {name: "66"};
},
template: '<div>66</div>'
});
var app = new Vue({
el: "#app"
});
//通過實例屬性$refs訪問子組件
app.$refs.my.name;
異步組件
將應用拆分多個小模塊,按需從服務器下載,Vue允許將組件定義為一個工廠函數(shù),動態(tài)解析,建議配合Webpack異步加載功能食用
<async-component></async-component>
Vue.component('async-component', function (resolve, reject) {
//模擬異步網(wǎng)絡請求
setTimeout(function () {
//調(diào)用porime模式resolve方法表示成功返回異步組件
resolve({template: '<div>async</div>'});
}, 1000);
});
var app = new Vue({
el: "#app"
});
組件命名約定
注冊組件(或props)使用kebab-case,camelCase,PascalCase,HTML模板中只能使用kebab-case
遞歸組件
設置了name選項,組件才可以遞歸調(diào)用自己
<my-component :tree="tree"></my-component>
Vue.component('my-component', {
props: ['tree'],
name: 'component',
template: '<div>' +
'<div v-if="tree instanceof Array">' +
'<component :tree="item" v-for="item in tree"></component>' +
'</div>' +
'<div v-else>{{tree}}</div>' +
'</div>'
});
var app = new Vue({
el: "#app",
data: {
tree: [[1, 2], 3]
}
});
要保證遞歸調(diào)用有終止條件(如v-if最終返回false),否則可能出現(xiàn)死循環(huán)
組件循環(huán)引用
A引用B,B中也引用A,使用Vue.component注冊的全局組件,框架會自動解決依賴組件的矛盾
X-Templates
<my-component></my-component>
<script type="text/x-template" id="my-component">
<div>hello</div>
</script>
Vue.component('my-component', {
template: '#my-component'
});
低開銷靜態(tài)組件使用v-once,緩存渲染結(jié)果
Vue.component('my-component', {
template: '<div v-once>message</div>'
});