關(guān)于vue組件的總結(jié)
注冊組件
vue中組件的注冊分為兩種:
- 全局注冊
- 局部注冊
全局注冊
- 全局注冊的組件在任何vue實(shí)例中都能使用
- 調(diào)用vue的
component
方法就可以全局注冊組件 -
component
方法接收兩個(gè)參數(shù):- 參數(shù)一:組件名
- 參數(shù)二:對象肛真,對象中包含了組件模板钾唬,data椭迎,methods等屬性焙矛,與vue實(shí)例相同
Vue.component('g-div',{
template: `<div>這是一個(gè)自定義組件</div>`,
data () {
return {
message: 'hello world'
}
}
})
上面代碼定義了一個(gè)名為g-div的自定義組件炼蹦,然后可以在DOM元素中的vue實(shí)例中使用它了
<div id="app">
<g-div></g-div>
</div>
<script>
let app = new Vue({
el: '#app'
})
</script>
局部注冊
- 局部注冊只能在vue實(shí)例中進(jìn)行
- 局部注冊的組件只有它所在的vue實(shí)例可以使用
- 在vue實(shí)例中定義
component
屬性就可以進(jìn)行局部注冊
let app = new Vue({
el: '#app',
data: {
},
components: {
'my-component': {
template: `<div>這是局部注冊的組件逮壁,只能在當(dāng)前Vue實(shí)例中使用</div>`,
}
}
})
ps:組件的模板中只允許有一個(gè)根元素,所有的元素都必須由一個(gè)元素包裹徐矩,否則編譯時(shí)會(huì)報(bào)錯(cuò)
組件中的data
組件中的data必須是一個(gè)函數(shù)滞时,這個(gè)函數(shù)返回一個(gè)對象,對象里面就是你所寫的數(shù)據(jù)B说啤F夯!
為什么組件中的data必須是一個(gè)函數(shù)呢鳞骤?
因?yàn)樵陧撁嬷锌赡軙?huì)多次復(fù)用這個(gè)組件窒百,如果data是一個(gè)對象,那么所有組件共享這個(gè)對象豫尽,任一組件的data發(fā)生變化便會(huì)影響到其他組件篙梢。
若組件中的data是一個(gè)返回對象的函數(shù),那么每次使用組件時(shí)便會(huì)創(chuàng)建一個(gè)新的對象美旧,達(dá)到每個(gè)組件對應(yīng)一個(gè)對象庭猩,不會(huì)產(chǎn)生因數(shù)據(jù)共享而衍生的bug。
組件受限
在某些情況下我們無法正常的使用組件陈症,比如當(dāng)組件的父元素的ul
蔼水、table
時(shí),我們無法正常使用組件录肯,因?yàn)檫@些元素的內(nèi)部只能寫入特定的子元素趴腋,比如table
中只允許存在tbody
、td
等子元素论咏。
可我就是想在這些元素內(nèi)部使用組件怎么辦优炬?
答:使用is屬性
<table>
<tbody is="my-component"></tbody>
</table>
父組件與子組件的溝通:props
在很多場景下,我們需要父組件向子組件傳遞自身的數(shù)據(jù)厅贪,這個(gè)時(shí)候我們需要在子組件中定義props
屬性來接收數(shù)據(jù)蠢护。
props
傳遞數(shù)據(jù)是單向的,只能由父組件向子組件傳遞數(shù)據(jù)养涮。
<div id="app">
<g-div :parent-message="parentMessage"><g-div>
</div>
<script>
Vue.component('g-div',{
template: `<div>{{parentMessage}}</div>`,
props: ['parentMessage'] //已經(jīng)得到了數(shù)據(jù)葵硕,可以直接使用
})
new Vue({
el: '#app',
data:{
parentMessage: '我是父組件傳遞的數(shù)據(jù)'
}
})
</script>
我們也可以將接收到的數(shù)據(jù)保存到子組件的data中眉抬。
Vue.component('g-div',{
template: `<div>{{parentMessage}}</div>`,
props: ['parentMessage'],
data(){
return {
childMessage:this.parentMessage //可以通過this.xxx來獲取接收到的數(shù)據(jù)
}
}
})
如果props作為需要被轉(zhuǎn)變的原始值傳入,這種情況下我們可以使用計(jì)算屬性
<div id="app">
<input type="text" v-model="width">
<g-div :width="width"></g-div>
</div>
<script>
Vue.component('g-div',{
props:['width'],
template: `<div :style="style">我的寬度是{{width}}px</div>`,
data(){
return{
}
},
computed:{
style(){
return {
width: this.width + 'px',
background: 'red',
height: '50px'
}
}
}
})
new Vue({
el: '#app',
data:{
width: 0
}
})
</script>
大小寫的問題
在定義的組件懈凹,組件名若使用駝峰命名蜀变,在html中要使用短橫線命名。
<div id="app">
<my-div></my-div>
<!--如果使用<myDiv></myDiv>會(huì)報(bào)錯(cuò)-->
</div>
<script>
Vue.component('myDiv',{
template:`<div>我是一個(gè)組件</div>`
})
new Vue({
el: '#app'
})
</script>
在組件中介评,父組件給子組件傳遞數(shù)據(jù)必須使用短橫線库北,在template中使用文本插值的形式時(shí),只能使用駝峰们陆,其他書寫方式可能會(huì)產(chǎn)生錯(cuò)誤寒瓦。
在組件的data中,若是使用this.xxx接收數(shù)據(jù)坪仇,必須使用駝峰命名孵构,不能使用短橫線命名。
數(shù)據(jù)驗(yàn)證
props可以驗(yàn)證傳遞進(jìn)來的數(shù)據(jù)類型
<div id="app">
<my-component :a="a" :b="b"></my-component>
</div>
<script>
Vue.component('my-component',{
props:{
a: Number,
//如果數(shù)據(jù)類型不相同烟很,頁面任然會(huì)渲染,同時(shí)控制臺會(huì)報(bào)錯(cuò)
b: [String,Number],
c: {
type: [Boolean,Number,String,Array],
required: true, //指定為必傳蜡镶,不傳會(huì)報(bào)錯(cuò)
},
d: {
type: String,
default: 'hello vue' //指定默認(rèn)值
}
},
template:`<div>{{a}} {雾袱} {{c}} {lx8wolq}</div>`
})
new Vue({
el: '#app',
data:{
a: 1,
b: '123',
c: 666,
d: 'zink'
}
})
</script>
當(dāng)你需要驗(yàn)證數(shù)據(jù)類型時(shí),props必須是對象官还。
自定義事件--子組件給父組件傳遞數(shù)據(jù)
使用v-on除了監(jiān)聽DOM事件外芹橡,還可以用于組件之間的自定義事件,
JS的設(shè)計(jì)模式——觀察者模式望伦,dispatchEvent
和addEventListentr
這兩個(gè)方法林说,前者用于觸發(fā)事件,后者用于添加事件監(jiān)聽屯伞,vue組件也有與之類似的模式腿箩,子組件用$emit
來觸發(fā)事件,父組件用$on
來監(jiān)聽子組件事件劣摇。
<div id="app">
當(dāng)前余額為:{{balance}}
<son-component :sum="balance" @change="changeBalance"></son-component>
</div>
<script>
Vue.component('son-component',{
props: ['sum'],
template:`<div>
<button @click="addNum">+1000</button>
<button @click="reduceNum">-1000</button>
</div>`,
data(){
return {
number: this.sum
}
},
methods:{
addNum(){
this.number += 1000
this.$emit('change',this.number)
},
reduceNum(){
this.number -= 1000
this.$emit('change',this.number)
}
}
})
new Vue({
el: '#app',
data:{
balance: 2000
},
methods:{
changeBalance(value){
this.balance = value
}
}
})
</script>
第一步:自定義事件
第二步:在子組件中用$emit
觸發(fā)事件珠移,第一個(gè)參數(shù)為事件名,后面的參數(shù)為要傳遞的數(shù)據(jù)末融。
第三部:在父組件中監(jiān)聽這個(gè)事件
子組件間的通信
子組件之間的通信需要在根組件中定義一個(gè)新的vue實(shí)例作為我們的事件承載中心。
<div id="app">
<a-component></a-component>
<b-component></b-component>
</div>
<script>
Vue.component('a-component',{
template: `
<button @click="delivery">點(diǎn)擊我向B中傳遞數(shù)據(jù)</button>
`,
data(){
return{
msg: '這是來自A組件的數(shù)據(jù)'
}
},
methods:{
delivery(){
this.$root.bus.$emit('test',this.msg)
}
}
})
Vue.component('b-component',{
template:`<div>我是B組件,這是我接收到的數(shù)據(jù):{{msg}}</div>`,
data(){
return {
msg: undefined
}
},
created:function(){
//這里要使用箭頭函數(shù)勾习,否則this指向的是bus,箭頭函數(shù)的this繼承作用域鏈父級
this.$root.bus.$on('test',(value)=>{
console.log(this)
this.msg = value
})
}
})
let app = new Vue({
el: '#app',
data:{
bus: new Vue()
}
})
</script>
箭頭函數(shù)沒有this巧婶,只會(huì)從自己的作用域鏈的上一層繼承this
父鏈:this.$parent
涂乌,在子組件中使用,可以獲取到父組件的內(nèi)容
<div id="app">
<son-component></son-component>---{{msg}}
</div>
<script>
Vue.component('son-component',{
template: `
<button @click="change">點(diǎn)擊我修改數(shù)據(jù)</button>
`,
methods:{
change(){
this.$parent.msg = "數(shù)據(jù)已經(jīng)修改"
}
}
})
let app = new Vue({
el: '#app',
data:{
msg: '數(shù)據(jù)還未修改'
}
})
</script>
子鏈:this.$refs
<div id="app">
<button @click="getSon">點(diǎn)擊我獲取子組件的內(nèi)容</button>---{{msg}}
<a-component ref="a"></a-component>
<b-component ref="b"></b-component>
<c-component ref="c"></c-component>
</div>
<script>
Vue.component('a-component',{
template: `
<div></div>
`,
data(){
return{
msg:'我是子組件a的內(nèi)容'
}
}
})
Vue.component('b-component',{
template: `
<div></div>
`,
data(){
return{
msg:'我是子組件b的內(nèi)容'
}
}
})
Vue.component('c-component',{
template: `
<div></div>
`,
data(){
return{
msg:'我是子組件c的內(nèi)容'
}
}
})
let app = new Vue({
el: '#app',
data:{
msg: '數(shù)據(jù)還未修改'
},
methods:{
getSon(){
console.log(this.$refs)
this.msg = this.$refs.c.msg
}
}
})
</script>
插槽
為了讓組件可以組合骂倘,我們需要一種方式來混和父組件的內(nèi)容與子組件自己的模板巴席。這個(gè)過程被稱為內(nèi)容分發(fā)历涝,vue.js實(shí)現(xiàn)了一個(gè)內(nèi)容分發(fā)API,使用slot
元素作為原始內(nèi)容的插槽漾唉。
首先我們需要明確內(nèi)容在那個(gè)作用域里編譯荧库,假設(shè)模板為
<div id="app">
<my-component>
{{msg}}
</my-component>
</div>
msg是綁定到父組件的數(shù)據(jù)還是綁定到子組件的數(shù)據(jù)?答案是父組件赵刑。組件作用域簡單地說就是:
父組件模板的內(nèi)容在父組件作用域內(nèi)編譯
子組件模板的內(nèi)容在子組件作用域內(nèi)編譯
讓我們再回到上面的代碼分衫,所以此時(shí)app內(nèi)的所有內(nèi)容,都在父組件作用域內(nèi)
插槽的用法
插槽的作用:父組件的內(nèi)容和子組件相混合般此,從而彌補(bǔ)視圖的不足蚪战,說明白了就是子組件內(nèi)可以寫入父組件的內(nèi)容。
<div id="app">
<my-component>
{{msg}}
</my-component>
</div>
<script>
Vue.component('my-component',{
template:`<div>
<slot>如果父組件沒有插入內(nèi)容铐懊,我會(huì)默認(rèn)出現(xiàn)</slot>
</div>`,
data(){
return{
msg: '我是子組件的內(nèi)容'
}
}
})
new Vue({
el:'#app',
data:{
msg:"我是父組件的內(nèi)容"
}
})
</script>
具名插槽
在slot
標(biāo)簽上添加name屬性邀桑,在插入的標(biāo)簽上添加slot="xxx"
<div id="app">
<my-component>
<h2 slot="header">我是標(biāo)題</h2>
<p slot="main">我是正文</p>
<p slot="main">正文第二部分</p>
<span slot="footer">我是底部信息</span>
</my-component>
</div>
<script>
Vue.component('my-component',{
template:`<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot name="main"></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>`,
data(){
return{
msg: '我是子組件的內(nèi)容'
}
}
})
new Vue({
el:'#app',
data:{
msg:"我是父組件的內(nèi)容"
}
})
</script>
此時(shí)渲染出來的DOM結(jié)構(gòu)如下:
![DOM結(jié)構(gòu)](https://i.loli.net/2018/09/10/5b954d9d53bdf.png)
作用域插槽
作用:從子組件獲取數(shù)據(jù)
<div id="app">
<my-component>
<!--template不會(huì)被渲染,vue2.5.0之后不需要寫template標(biāo)簽了-->
<template slot="son" slot-scope="prop">
{{prop.text}}
<!--無法拿到name-->
</template>
</my-component>
</div>
<script>
Vue.component('my-component',{
template:`
<div>
<slot name="son" text="hello vue">
</slot>
</div>
`
})
new Vue({
el:'#app'
})
</script>
訪問slot
通過this.$slot訪問
<div id="app">
<my-component>
<h2 slot="header">我是標(biāo)題</h2>
<p slot="main">我是正文</p>
<p slot="main">正文第二部分</p>
<span slot="footer">我是底部信息</span>
</my-component>
</div>
<script>
Vue.component('my-component',{
template:`<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot name="main"></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>`,
data(){
return{
msg: '我是子組件的內(nèi)容'
}
},
mounted(){
//訪問插槽
let header = this.$slots.header
let text = header[0].elm.innerText
console.log(header)
console.log(text)
}
})
new Vue({
el:'#app',
data:{
msg:"我是父組件的內(nèi)容"
}
})
</script>
動(dòng)態(tài)組件
Vue給我們提供了一個(gè)元素叫component
科乎,用來動(dòng)態(tài)的掛載不同的組件
實(shí)現(xiàn):使用is特性來實(shí)現(xiàn)
<div id="app">
<component :is="thisView"></component>
<button @click="changeView('a')">a</button>
<button @click="changeView('b')">b</button>
<button @click="changeView('c')">c</button>
</div>
<script>
Vue.component('a-component',{
template:`<div>我是a組件的內(nèi)容</div>`
})
Vue.component('b-component',{
template:`<div>我是b組件的內(nèi)容</div>`
})
Vue.component('c-component',{
template:`<div>我是c組件的內(nèi)容</div>`
})
new Vue({
el:'#app',
data:{
thisView: 'a-component'
},
methods:{
changeView(value){
this.thisView = value+'-component'
}
}
})
</script>