Vue 組件間通信
1. 前言
本小節(jié)我們將介紹組件間是如何實現(xiàn)數(shù)據(jù)通信的。包括父組件向子組件、子組件向父組件、兄弟組件、非關系組件之間的數(shù)據(jù)通信。組件通信是組件式開發(fā)中非常重要的一部分,也是組件式開發(fā)中的難點。在學完本小節(jié)之后,同學們可以通過反復地編寫組件來加深印象。
2. 慕課解釋
組件是 vue 最強大的功能之一,而組件實例的作用域是相互獨立的,這就意味著不同組件之間的數(shù)據(jù)無法相互引用。我們需要使用特定的方式來實現(xiàn)組件間的數(shù)據(jù)通信,接下來讓我們一個個介紹這幾種類別的組件通信是如何實現(xiàn)的。
3. 父組件通過 props 傳遞數(shù)據(jù)給子組件
父組件通過 props
屬性向子組件傳遞數(shù)據(jù)。子組件利用組件實例的 props
屬性定義組件需要接收的參數(shù),在使用組件時通過 attribute
的方式傳入?yún)?shù),如:
// 在子組件內(nèi)定義組件接收一個參數(shù) name
{
props: ['name']
}
// 父組件使用組件時傳遞參數(shù) name
<child :name="name"></child>
接下來我們看一個具體示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component('parent', {
template: '<child :name="name"></child>',
data() {
return {
name: '句號'
}
}
})
Vue.component('child', {
template: '<div>{{name}}</div>',
props: ['name']
})
var vm = new Vue({
el: '#app',
data() {
return {}
}
})
</script>
</html>
代碼解釋
JS 代碼第 14-18 行:定義了組件 child,并用 props 接收一個參數(shù) name。
JS 代碼第 4-12 行:定義了組件 parent,在組件中使用 <child></child>
引用組件,并用 attribute 的方式將 name 傳遞給組件 child。
在上面的例子中,組件 Child
接收參數(shù) name
,name
可以是字符串、數(shù)組、布爾值、對象等類型。但有時候我們需要給接收的參數(shù)指定一個特殊的類型和默認值,接下來我們就來介紹一下如何指定 props 的類型和默認值。
3.1 定義props的類型和默認值
在上面的例子中,props 接收一個組件參數(shù)數(shù)組。實際上,props 也可以接收一個對象,對象key
為組件接收參數(shù)的參數(shù)名,其值是一個對象,屬性 type
用來指定參數(shù)的類型,屬性 default
用來指定參數(shù)的默認值:
{
props: {
name: {
type: String,
default: '句號'
}
}
}
接下來我們看一個具體示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component('parent', {
template: '<div><child :name="name" /> <child/></div>',
data() {
return {
name: '慕課網(wǎng)'
}
}
})
Vue.component('child', {
template: '<div>{{name}}</div>',
props: {
name: {
type: String,
default: '句號'
}
}
})
var vm = new Vue({
el: '#app',
data() {
return {}
}
})
</script>
</html>
JS 代碼第 11-19 行:定義了組件 child,并用 props 接收一個字符串類型的參數(shù) name,其默認值是:句號。
JS 代碼第 3-10 行:定義了組件 parent,在組件中使用 <child></child>
兩次引用組件,<child :name="name" />
的方式傳遞 name 值,<child/>
使用默認的 name 值。
TIPS: 注意,給數(shù)組和對象類型的
props
設置默認值的時候,需要按照以下的寫法:
props: {
detail: {
type: Object,
default: () => {
return {
name: '句號'
}
}
},
loves: {
type: Array,
default: () => {
return []
}
}
}
4. 子組件通過 $emit 傳遞數(shù)據(jù)給父組件
介紹完父組件傳遞數(shù)據(jù)給子組件的方式,我們再來看看子組件是如何傳遞數(shù)據(jù)給父組件的。
子組件通過 $emit
傳遞事件給父組件,父組件通過 $on
監(jiān)聽事件:
// 子組件定義事件
this.$emit('事件名稱', '傳遞的參數(shù)') //例: this.$emit('add', 111)
// 父組件監(jiān)聽事件的觸發(fā)
<child @事件名稱="事件觸發(fā)的方法"/>
具體示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component('parent', {
template: '<div><child :name="name" :count="count" @add="add"/></div>',
data() {
return {
name: '句號',
count: 18
}
},
methods: {
// 父組件通過 @事件名 監(jiān)聽
// count 表示事件觸發(fā)傳遞的參數(shù)
add(count) {
this.count = count
}
}
})
Vue.component('child', {
template: '<div>我是:{{name}}, 我今年 {{count}}歲。<button @click="add">加一歲</button></div>',
props: {
name: {
type: String,
default: '句號'
},
count: {
type: Number,
default: 18
}
},
methods: {
add(){
// add -> 觸發(fā)的事件名
// this.count + 1 -> 觸發(fā)事件時傳遞的參數(shù)
this.$emit('add', this.count + 1)
}
}
})
var vm = new Vue({
el: '#app',
data() {
return {}
}
})
</script>
</html>
代碼解釋
JS 代碼第 19-38 行:定義了組件 child,該組件接收兩個參數(shù):1. 字符串類型的 name,默認值為:句號。2. 數(shù)字類型的 age,默認值為 18。組件模版中,通過按鈕點擊事件觸發(fā) add 方法,該方法內(nèi)部通過 $emit
觸發(fā)事件 add,并將 age + 1 的值作為參數(shù)傳遞。
JS 代碼第 3-18 行:定義了組件 parent,在組件中使用 <child :name="name" :age="age" @add="add"/>
引用組件,并綁定 add 事件,當事件 add 觸發(fā)時調(diào)用 methods 中的 add 函數(shù)。
5. 非父子組件間數(shù)據(jù)傳遞
前面我們介紹了具有父子關系的組件是如何進行數(shù)據(jù)傳遞的。但實際上,并不是所有的組件都是父子關系,組件間還有兄弟組件、子孫組件、無關系組件,那么這些組件間是如何進行通信的呢?
相信在學完本節(jié)前面的內(nèi)容之后這個問題并不能難倒大家。
對于兄弟組件的數(shù)據(jù)通信:它們有共同的父組件,我們可以通過父組件傳遞的方式實現(xiàn)數(shù)據(jù)通信。
對于子孫組件的數(shù)據(jù)通信:可以通過 props 的方式向下逐層傳遞下去,也可以通過 $emit 將事件向上逐層傳遞。
對于非關系組件的數(shù)據(jù)通信:通過使用一個空的Vue實例作為中央事件總線。
5.1 通過公有的父組件進行非父子組件間的通信
假設現(xiàn)在有三個組件分別是<Parent>
、<ChildA>
、<ChildB>
,其中組件<Parent>
是<ChildA>
和<ChildB>
的父組件,<ChildA>
和<ChildB>
為兄弟組件,<ChildA>
和<ChildB>
組件間的通信可以借助<Parent>
來間接傳遞。它的流程大致是這樣:
<ChildA>
通過$emit
將數(shù)據(jù)傳遞給<Parent>
,<Parent>
再通過props
將數(shù)據(jù)傳遞給<ChildB>
。
具體示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<person @modify="modify"></person>
<detail :name="name" :count="count"/>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component('person', {
template: '<div><div>姓名:<input type="text" v-model="name"/></div><div>年齡:<input type="text" v-model="count"/></div><button @click="modify">修改</button></div>',
data() {
return {
name: '句號',
count: 18
}
},
methods: {
modify() {
this.$emit('modify', {name: this.name, count: this.count})
}
}
})
Vue.component('detail', {
template: '<div>我是:{{name}}, 我今年 {{count}}歲。</div>',
props: {
name: {
type: String,
default: '句號'
},
count: {
type: Number,
default: 18
}
},
methods: {
}
})
var vm = new Vue({
el: '#app',
data() {
return {
name: '句號',
count: 18
}
},
methods: {
modify(detail) {
this.name = detail.name
this.count = parseInt(detail.count)
}
}
})
</script>
</html>
代碼解釋
JS 代碼第 18-30 行:定義了組件 detail,它從父組件接收 name 和 age 兩個參數(shù)。
JS 代碼第 3-17 行:定義了組件 person,它通過 $emit 將組件內(nèi)輸入的 name 和 age 傳遞給父組件。
JS 代碼第 38-41 行:接收了組件 person 傳遞過來的事件,并修改 name 和 age。
HTML 代碼第 3 行:將 name 和 age 傳遞給組件 detail。
5.2 通過使用一個空的 Vue 實例作為中央事件總線
在Vue
中可以使用 EventBus
來作為溝通橋梁的概念,就像是所有組件共用相同的事件中心,可以向該中心注冊發(fā)送事件或接收事件,所以組件都可以上下平行地通知其他組件。
首先我們需要做的是創(chuàng)建事件總線,并將它掛載到Vue
原型上,在實例中通過this.bus.$emit
發(fā)送事件,通過this.bus.$on
接收事件。
// 定義事件總線
let bus = new Vue()
Vue.prototype.bus = bus
// 定義發(fā)送事件
this.bus.$emit('事件名稱', data)
// 定義接收事件 并在回調(diào)中接收參數(shù)
this.bus.$on('事件名稱', (data) => {
})
接下來我們看一段具體示例代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<person></person>
<detail />
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script type="text/javascript">
let bus = new Vue()
Vue.prototype.bus = bus
Vue.component('person', {
template: '<div><div>姓名:<input type="text" v-model="name"/></div><div>年齡:<input type="text" v-model="count"/></div><button @click="modify">修改</button></div>',
data() {
return {
name: '句號',
count: 18
}
},
methods: {
modify() {
this.bus.$emit('modify', {name: this.name, count: this.count})
}
}
})
Vue.component('detail', {
template: '<div>我是:{{name}}, 我今年 {{count}}歲。</div>',
data() {
return {
name: '句號',
count: 18
}
},
mounted() {
this.bus.$on('modify', (detail) => {
this.name = detail.name
this.count = detail.count
})
}
})
var vm = new Vue({
el: '#app',
methods: {
}
})
</script>
</html>
代碼解釋
JS 代碼第 3-4 行:通過 new Vue() 創(chuàng)建一個 vue 實例,并將它掛載在 Vue 的原型上。這樣,在 vue 組件中可以通過 this.bus
訪問到這個實例對象。
JS 代碼第 5-18 行:定義了組件 person,當點擊修改按鈕的時候通過 this.bus.$emit
發(fā)送一個名為 modify
的事件,并將組件內(nèi)輸入的 name 和 age 作為參數(shù)傳遞。
JS 代碼第 19-33 行:定義組件 detail,在組件內(nèi)部通過 this.bus.$on
監(jiān)聽名為 modify 的事件,當事件觸發(fā)時執(zhí)行修改操作。
6. 小結(jié)
在本小節(jié),我們介紹了組件間的通信方式,主要有以下知識點:
- 父組件通過
props
向子組件傳遞參數(shù)進行數(shù)據(jù)通信; - 子組件通過
$emit
向父組件傳遞事件進行數(shù)據(jù)通信; - 兄弟組件通過共同父組件進行數(shù)據(jù)通信;
- 通過使用一個空的 Vue 實例作為中央事件總線進行非關系層組件的數(shù)據(jù)通信。