7.1 使用組件的原因
作用:提高代碼的復(fù)用性
7.2 組件的使用方法
1. 全局注冊
Vue.component('my-component',{
template:'<div>我是組件的內(nèi)容</div>'
})
var app = new Vue({
el:'#app',
data: {
}
})
優(yōu)點:所有的Vue實例都可以用
缺點:權(quán)限太大,容錯率降低
2. 局部注冊
var app = new Vue({
el:'#app',
components:{
'my-component':{
template: '<div>我是組件的內(nèi)容</div>'
}
}
})
3. vue組件的模板在某些情況下會受到html標(biāo)簽的限制
比如 <table>
中只能還有 <tr>
, <td>
這些元素,所以直接在table中使用組件是無效的,此時可以使用is
屬性來掛載組件
<table>
<tbody is="my-component"></tbody>
</table>
7.3 組件使用的奇淫技巧
- 推薦使用小寫字母加-進行命名(必須) child, my-componnet命名組件
-
template
中的內(nèi)容必須被一個DOM元素包括 ,也可以嵌套 - 在組件的定義中,除了
template
之外的其他選項—data
,computed
,methods
-
data
必須是一個方法
var app = new Vue({
el:'#app',
data: {},
components: {
'btn-component': {
template: '<button @click="count++">{{count}}</button>',
data: function(){
return {
count: 0
}
}
}
}
})
7.4 使用props傳遞數(shù)據(jù) 父親向兒子傳遞數(shù)據(jù)
- 在組件中使用
props
來從父親組件接收參數(shù),注意,在props
中定義的屬性,都可以在組件中直接使用 -
props
來自父級,而組件中data return的數(shù)據(jù)就是組件自己的數(shù)據(jù),兩種情況作用域就是組件本身,可以在template
,computed
,methods
中直接使用 -
props
的值有兩種,一種是字符串?dāng)?shù)組,一種是對象 - 可以使用
v-bind
動態(tài)綁定父組件來的內(nèi)容
7.5 單向數(shù)據(jù)流
- 解釋 : 通過
props
傳遞數(shù)據(jù)都是單向的了, 也就是父組件數(shù)據(jù)變化時會傳遞給子組件,但是反過來不行。 - 目的 :是盡可能將父子組件解稿,避免子組件無意中修改了父組件的狀態(tài)。
- 應(yīng)用場景: 業(yè)務(wù)中會經(jīng)常遇到兩種需要改變 prop 的情況
- 一種是父組件傳遞初始值進來,子組件將它作為初始值保存起來,在自己的作用域下可以隨意使用和修改。這種情況可以在組件
data
內(nèi)再聲明一個數(shù)據(jù),引用父組件的prop
- 步驟一:注冊組件
- 步驟二:將父組件的數(shù)據(jù)傳遞進來,并在子組件中用props接收
- 步驟三:將傳遞進來的數(shù)據(jù)通過初始值保存起來
<div id="app">
<my-comp init-count="666"></my-comp>
</div>
<script>
var app = new Vue({
el:'#app',
components:{
'my-comp':{
props:['init-count'],
template:'<div>{{init-count}}</div>',
data:function () {
return{
count:this.initCount
}
}
}
}
})
</script>
- 另一種情況就是 prop 作為需要被轉(zhuǎn)變的原始值傳入。這種情況用計算屬性就可以了
- 步驟一:注冊組件
- 步驟二:將父組件的數(shù)據(jù)傳遞進來,并在子組件中用props接收
- 步驟三:將傳遞進來的數(shù)據(jù)通過計算屬性進行重新計算
<div id="app">
<inputtype="text" v-model="width">
<my-comp :width="width"></my-comp>
</div>
--------------------------------------------
var app = new Vue({
el:'#app',
data:{
width:''
},
components:{
'my-comp':{
props:['init-count','width'],
template:'<div :style="style">{{init-count}}</div>',
computed:{
style:function () {
return{
width:this.width+'px',
background:'red'
}
}
}
}
}
})
7.6 數(shù)據(jù)驗證
- @ vue組件中camelCased (駝峰式) 命名與 kebab-case(短橫
線命名) - @ 在html中, myMessage 和 mymessage 是一致的,因此在組件中的html
中使用必須使用kebab-case(短橫線)命名方式。在html中不允許使用駝
峰!!!!!! - @ 在組件中, 父組件給子組件傳遞數(shù)據(jù)必須用短橫線。在template中,必
須使用駝峰命名方式,若為短橫線的命名方式。則會直接保錯。 - @ 在組件的data中,用this.XXX引用時,只能是駝峰命名方式。若為短橫線
的命名方式,則會報錯。
驗證的 type 類型可以是:
? String
? Number
? Boolean
? Object
? Array
? Function
Vue.component ( ’ my-compopent ’, {
props : {
//必須是數(shù)字類型
propA : Number ,
//必須是字符串或數(shù)字類型
propB : [String , Number] ,
//布爾值,如果沒有定義,默認(rèn)值就是 true
propC: {
type : Boolean ,
default : true
},
//數(shù)字,而且是必傳
propD: {
type: Number ,
required : true
},
//如果是數(shù)組或?qū)ο螅J(rèn)值必須是一個函數(shù)來返回
propE: {
type : Array ,
default : function () {
return [] ;
}
},
//自定義一個驗證函數(shù)
propF: {
validator : function (value) {
return value > 10;
}
}
}
});
7.7 組件通信
組件關(guān)系可分為父子組件通信、兄弟組件通信、跨級組件通信
7.7.1 自定義事件—子組件給父組件傳遞數(shù)據(jù)
使用v-on 除了監(jiān)昕 DOM 事件外,還可以用于組件之間的自定義事件。
JavaScript 的設(shè)計模式 一一觀察者模式, dispatchEvent 和 addEventListener這兩個方法。 Vue 組件也有與之類似的一套模式,子組件用on()來 監(jiān)昕子組件的事件 。
直接甩代碼
- 第一步:自定義事件
- 第二步: 在子組件中用$emit觸發(fā)事件,第一個參數(shù)是事件名,后邊的參數(shù)是要傳遞的數(shù)據(jù)
- 第三步:在自定義事件中用一個參數(shù)來接受
<div id="app">
<p>您好,您現(xiàn)在的銀行余額是{{total}}元</p>
<btn-compnent@change="handleTotal"></btn-compnent>
</div>
<script src="js/vue.js"></script>
<script>
//關(guān)于
var app = new Vue({
el:'#app',
data:{
total:0
},
components:{
'btn-compnent':{
template:'<div>\
<button @click="handleincrease">+1</button> \
<button @click="handlereduce">-1</button>\
</div>',
data:function(){
return {
count:0
}
},
methods:{
handleincrease :function () {
this.count++;
this.$emit('change',this.count);
},
handlereduce:function () {
this.count--;
this.$emit('change',this.count);
}
}
}
},
methods:{
handleTotal:function (total) {
this.total = total;
}
}
})
</script>
7.7.2 在組件中使用v-model
$emit的代碼,這行代碼實際上會觸發(fā)一個 input事件, ‘input’后的參數(shù)就是傳遞給v-model綁定的屬性的值
v-model 其實是一個語法糖,這背后其實做了兩個操作
- v--bind 綁定一個 value 屬性
- v--on 指令給當(dāng)前元素綁定 input 事件
要使用v-model,要做到:
接收一個 value 屬性。
在有新的 value 時觸發(fā) input 事件
<div id="app">
<p>您好,您現(xiàn)在的銀行余額是{{total}}元</p>
<btn-compnent v-model="total"></btn-compnent>
</div>
<script src="js/vue.js"></script>
<script>
//關(guān)于
var app = new Vue({
el:'#app',
data:{
total:0
},
components:{
'btn-compnent':{
template:'<div>\
<button @click="handleincrease">+1</button> \
<button @click="handlereduce">-1</button>\
</div>',
data:function(){
return {
count:0
}
},
methods:{
handleincrease :function () {
this.count++;
----------------------注意觀察.這一行,emit的是input事件----------------
-
this.$emit('input',this.count);
},
handlereduce:function () {
this.count--;
this.$emit('input',this.count);
}
}
}
}
})
</script>
7.7.3 非父組件之間的通信
<div id="app" >
<my-acomponent></my-acomponent>
<my-bcomponent></my-bcomponent>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js">
</script>
<script>
Vue.component('my-acomponent',{
template:'<div><button @click="handle">點擊我向B組件傳遞數(shù)據(jù)</button></div>',
data:function () {
return{
aaa:'我是來自A組件的內(nèi)容'
}
},
methods:{
handle:function () {
this.$root.bus.$emit('lala',this.aaa);
}
}
})
Vue.component('my-bcomponent',{
template:'<div></div>',
created:function () {
//A組件在實例創(chuàng)建的時候就監(jiān)聽事件---lala事件
this.$root.bus.$on('lala',function (value) {
alert(value)
});
}
})
父鏈:this.$parent
Vue.component('child-component',{
template:'<button @click="setFatherData">通過點擊我修改父親的數(shù)據(jù)</button>',
methods:{
setFatherData:function () {
this.$parent.msg = '數(shù)據(jù)已經(jīng)修改了'
}
}
})
子鏈:this.$refs
提供了為子組件提供索引的方法,用特殊的屬性ref為其增加一個索引
var app = new Vue({
el:'#app',
data:{
//bus中介
bus:new Vue(),
msg:'數(shù)據(jù)還未修改',
formchild:'還未拿到'
},
methods:{
getChildData:function () {
//用來拿子組件中的內(nèi)容 ---- $refs
this.formchild = this.$refs.c.msg;
}
}
})
7.8.1 什么是slot(插槽)
為了讓組件可以組合,我們需要一種方式來混合父組件的內(nèi)容與子組件自己的模板。這個過程被稱為 內(nèi)容分發(fā).Vue.js 實現(xiàn)了一個內(nèi)容分發(fā) API,使用特殊的 ‘slot’ 元素作為原始內(nèi)容的插槽。
7.8.2 編譯的作用域
在深入內(nèi)容分發(fā) API 之前,我們先明確內(nèi)容在哪個作用域里編譯。假定模板為:
<child-component>
{{ message }}
</child-component>
message 應(yīng)該綁定到父組件的數(shù)據(jù),還是綁定到子組件的數(shù)據(jù)?答案是父組件。組件作用
域簡單地說是:
- 父組件模板的內(nèi)容在父組件作用域內(nèi)編譯;
- 子組件模板的內(nèi)容在子組件作用域內(nèi)編譯。
7.8.3 插槽的用法
父組件的內(nèi)容與子組件相混合,從而彌補了視圖的不足混合父組件的內(nèi)容與子組件自己的模板
單個插槽:
<div id="app" >
<my-component>
<p>我是父組件的內(nèi)容</p>
</my-component>
</div>
Vue.component('my-component',{
template:'<div>\
<slot>\
如果父組件沒有插入內(nèi)容,我就作為默認(rèn)出現(xiàn)\
</slot>\
</div>'
})
具名插槽:
<name-component>
<h3 slot="header">我是標(biāo)題</h3>
<p>我是正文內(nèi)容</p>
<p>正文內(nèi)容有兩段</p>
<p slot="footer">我是底部信息</p>
</name-component>
Vue.component('name-component',{
template:'<div>\
<div class="header">\n' +
'<slot name="header">\n' +
'\n' +
'</slot>\n' +
'</div>\n' +
'<div class="contatiner">\n' +
'<slot>\n' +
' \n' +
'</slot>\n' +
'</div>\n' +
'<div class="footer">\n' +
'<slot name="footer">\n' +
'\n' +
'</slot> \n' +
'</div>'+
'</div>'
})
7.8.4 作用域插槽
作用域插槽是一種特殊的slot,使用一個可以復(fù)用的模板來替換已經(jīng)渲染的元素
——從子組件獲取數(shù)據(jù)
====template模板是不會被渲染的
Vue.component('my-component',{
template:'<div>\
<slot text="我是來自子組件的數(shù)據(jù)" ss="fdjkfjlsd" name="abc">\
</slot>\
</div>'
})
7.8.5 訪問slot
通過this.$slots.(NAME)
mounted:function () {
//訪問插槽
var header = this.$slots.header;
var text = header[0].elm.innerText;
var html = header[0].elm.innerHTML;
console.log(header)
console.log(text)
console.log(html)
}
7.9 組件高級用法–動態(tài)組件
VUE給我們提供 了一個元素叫component
作用是: 用來動態(tài)的掛載不同的組件
實現(xiàn):使用is特性來進行實現(xiàn)的
<div id="app" style="border: 1px solid; height: 340px">
Vue提供了一個元素叫component,再使用is特性來實現(xiàn) <br>
動態(tài):必須用到v-bind <br>
<br>
<hr>
<component v-bind:is="thisval"></component>
<button @click="handle('A')">我是A</button>
<button @click="handle('B')">我是B</button>
<button @click="handle('C')">我是C</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
thisval: 'compA'
},
methods: {
handle: function(val){
this.thisval = 'comp' + val
}
},
components: {
compA: {
template: '<div>我是A</div>'
},
compB: {
template: '<div>我是B</div>'
},
compC: {
template: '<div>我是C</div>'
}
}
})
</script>