vue api強化學習之-------組件之間通信
除去vuex之外的方式,即vue內部組件之間的通信方式
props 和 $emit
props 是單向數據流方式,數據只能通過 props 由父組件流向子組件,而子組件並不能通過修改 props 傳過來的數據修改父組件的相應狀態
使用v-on在父組件引入子組件後的模板上監聽子組件中的事件,$emit 可以觸發使用v-on在父組件中監聽的事件;
代碼示例
- 父組件communication.vue
<template>
<div class="communication">
<div>我是父組件</div>
<div class="child">
<!-- 使用v-on監聽子組件中的事件 myevent -->
<child1 :arr="arr" @myevent="childEmit" ></child1>
<div><span>子組件傳遞給父組件的數據:</span><span>{{childmsg}}</span></div>
</div>
</div>
</template>
<script>
import Child1 from './child1.vue'
export default {
name: 'Communication',
data(){
return {
arr:[
{national:'蜀國',emperor:'劉備'},
{national:'吳國',emperor:'孫權'},
{national:'魏國',emperor:'曹操'},
],
childmsg:"",
}
},
components:{
Child1,
},
methods:{
// 子組件調用父組件的中的此方法
childEmit(pra){
this.childmsg=pra;
},
}
}
</script>
- 子組件 child1.vue
<template>
<div class="child1">
<div>我是child1</div>
<div>
<span>我是從父組件中接受到的數據</span>
<p v-for="item in paraentArr" :key="item.national">
<span>{{item.national}}:</span>
<span>{{item.emperor}}</span>
</p>
</div>
<button @click="toParent">向父組件傳遞數據</button>
</div>
</template>
<script>
export default {
name: 'Child1',
// 接受父組件傳遞過來的數據,並規定接受數據的類型
props:{
arr:{
type:Array,
default:[]
}
},
data(){
// 將props中接受到的數據緩存到此處是必要的,因爲你不能直接修改 props 的值
return {
paraentArr:this.arr
}
},
methods:{
toParent(){
// 在此處觸發父組件中監聽的事件myevent,並向父組件傳遞數據
this.$emit("myevent","父組件接受到了嗎?")
},
}
}
</script>
$parent 和 ref
通過this.chiildren可以獲得當前組件的直系子組件,不過獲取到的是一個實例數組,操作不太方便,利用子組件標籤的ref屬性,我們可以在父組件中直接使用this.$refs.xx獲取到子組件的數據和方法,
代碼示例
- 父組件communication.vue
<template>
<div class="communication">
<div>我是父組件</div>
<div class="child">
// 子組件綁定ref
<child2 ref="child2"></child2>
<button @click="toChild2">向子組件傳數據</button><br/>
<span>子組件傳遞過來的數據:{{fromchild2}}</span>
</div>
</div>
</template>
<script>
import Child2 from './child2.vue'
export default {
name: 'Communication',
data(){
return {
fromchild2:''
}
},
components:{
Child2
},
methods:{
// 通過$refs獲取子組件的方法,通過此方法向子組件傳遞數據
toChild2(){
this.$refs.child2.updatePra("父組件傳遞過來的值");
// 通過$children獲取子組件的方法,通過此方法向子組件傳遞數據
// 此處應該遍歷獲取到的vue實例數組,找到想要的子組件實例,因爲$children獲取到的子組件實例沒法保證順序
console.log(this.$children[1],".........this.$children....");
this.$children[1].updatePra('父組件傳遞過來的值>>>>>>>>')
},
// 子組件獲取到父組件的實例後,通過自方法向父組件傳遞數據
child2UpdateData(msg){
this.fromchild2 = msg;
}
}
}
</script>
- 子組件 child2.vue
<template>
<div class="child2">
<div>我是child2</div>
<div>
<span>我是從父組件傳遞過來的:</span>
<span>{{fromParent}}</span><br/>
<button @click="toparent">toparent</button>
</div>
</div>
</template>
<script>
export default {
name: 'Child2',
data(){
// 根級響應式屬性,全部在此處聲明
return {
fromParent:"",
}
},
methods:{
// 父組件通過此方法向子組件傳參
updatePra(msg){
this.fromParent = msg
},
// 通過$parent獲取父組件實例對象,並向父組件傳遞數據
toparent(){
this.$parent.child2UpdateData("child2傳遞過來的數據,父組件收到了嗎?")
}
}
}
</script>
$on 和 $emit
$on 監聽當前實例上的自定義事件
$emit 觸發當前實例上的自定義事件
on只能作用在一一對應的同一個組件實例上,因此要使用這兩個方法進行組件之間的通信,則需要引入一個空的vue實例
代碼示例
此示例是從child1組件向child2組件傳遞消息,若想要從child2向child1組件傳遞消息,按下面流程將觸發器跟監聽器再返回來寫一套即可;
- 事件處理中心(一個空的vue實例) eventHub.js
import Vue from 'vue';
// 創建一個vue實例,用於組件之間的信息傳遞
const eventHub = new Vue();
export default eventHub;
- 子組件child1.vue (child1與child2爲兄弟組件)
<template>
<div class="child1">
<div>我是child1</div>
// 從child1組件向child2組件傳遞消息,
<div>
<button @click="child1ToChild2">child1向child2發送信息</button>
</div>
</div>
</template>
<script>
import eventHub from './eventHub.js'
export default {
name: 'Child1',
methods:{
child1ToChild2(){
// 通過一個外部vue實例,此實例相當於一個事件處理中心,觸發一個自定義事件
eventHub.$emit("tochild2event",'從child1傳遞過來的消息');
}
}
}
</script>
- 子組件 child2.vue
<template>
<div class="child2">
<div>我是child2</div>
<div>
<div>
顯示從child1中獲取到的信息:{{fromchild1}}
</div>
</div>
</div>
</template>
<script>
import eventHub from './eventHub.js'
export default {
name: 'Child2',
data(){
// 根級響應式屬性,全部在此處聲明
return {
fromchild1:'',
}
},
// 在此處需要首先監聽事件
mounted(){
const that = this;
// 組件掛載之後開始監聽tochild2event事件,並進行回調函數的處理
eventHub.$on("tochild2event",function(msg){
that.fromchild1 = msg;
})
},
}
</script>
$attrs 和 $listeners
vm.listeners,包含了父作用域中的v-on事件監聽器,不包括.native修飾符修飾的
$attrs 和 attrs’ | v-on=’$listeners’將信息直接傳遞給其後代組件
$listeners 和 attrs 是向下傳遞數據,$listeners 是向下傳遞方法,底層組件中可直接手動調用 $listeners 對象裏的方法;
代碼示例
- 父組件communication.vue
<template>
<div class="communication">
<div>我是父組件</div>
<div>
<span>從孫子組件傳遞過來的信息:</span>
<span>{{fromGrandsonMsg}}</span>
</div>
<div class="child">
<!-- 使用v-on監聽子組件中的事件 myevent grandsonCompmsg和grandEvent都是給孫子組件的 -->
<child1 :arr="arr" @myevent="childEmit" :grandsonCompmsg="grandmsg" @grandEvent="grandCallMethod"></child1>
</div>
</div>
</template>
<script>
import Child1 from './child1.vue'
export default {
name: 'Communication',
data(){
return {
arr:[
{national:'蜀國',emperor:'劉備'},
{national:'吳國',emperor:'孫權'},
{national:'魏國',emperor:'曹操'},
],
grandmsg:"孫子,收到我的信息了嗎?",
fromGrandsonMsg:""
}
},
components:{
Child1,
},
methods:{
grandCallMethod(msg){
this.fromGrandsonMsg = msg;
}
}
}
</script>
- 子組件 child1.vue
<template>
<div class="child1">
<div>我是child1</div>
<div>
<span>我是從父組件中接受到的數據</span>
<p v-for="item in paraentArr" :key="item.national">
<span>{{item.national}}:</span>
<span>{{item.emperor}}</span>
</p>
</div>
<div>
<!-- 此處通過 v-bind='$attrs'和v-on='$listeners'向孫子組件傳遞數據和方法 -->
<child1-child v-bind="$attrs" v-on="$listeners"></child1-child>
</div>
</div>
</template>
<script>
import Child1Child from './child1_child.vue'
export default {
name: 'Child1',
// 爲true時,從父組件中傳入的,此組件沒有通過props接受的參數,都會自動成爲子組件根標籤上的屬性;此時 $attrs中也存在
// 若爲false,則沒有被props接受的參數,都回保存到 $attrs中去
inheritAttrs: false,
components:{
Child1Child
},
// 接受父組件傳遞過來的數據,並規定接受數據的類型
props:{
arr:{
type:Array,
default:[]
}
},
data(){
// 將props中接受到的數據緩存到此處是必要的,因爲你不能直接修改 props 的值
return {
paraentArr:this.arr
}
},
}
</script>
- 子組件 child1_child.vue
<template>
<div class="child1_child">
<div>我是child1的child</div>
<div>
<span>我是從爺爺組件中獲取到的信息:{{fromGrandfarherMsg}}</span>
</div>
<button @click="$listeners.grandEvent('爺爺,你收到我發的信息了吧')">我要給爺爺發信息</button>
</div>
</template>
<script>
export default {
name: 'Child1Child',
// 爲true時,從父組件中傳入的,此組件沒有通過props接受的參數,都會自動成爲子組件根標籤上的屬性;此時 $attrs中也存在
// 若爲false,則沒有被props接受的參數,都回保存到 $attrs中去
inheritAttrs: false,
// 接受爺爺組件傳遞過來的數據,並規定接受數據的類型
props:{
grandsonCompmsg:{
type:String,
default:""
}
},
data(){
// 將props中接受到的數據緩存到此處是必要的,因爲你不能直接修改 props 的值
return {
fromGrandfarherMsg:this.grandsonCompmsg
}
},
}
</script>
v-model 和 :xx.sync
v-model通常用在表單組件的雙向數據綁定,當然,組件之前的通信也可用v-model來實現,組件之間使用v-model綁定參數時,默認綁定在value屬性上,同時默認給綁定了input事件;如:相當於:
組件屬性可通過sync修飾符,從而使得組件之間可進行雙向的數據綁定,使用sync顯示符時,此處在綁定一個屬性foo時,默認綁定了一個update:foo的事件,如下所示:
代碼示例
- 父組件 communication.vue
<template>
<div class="communication">
<div>我是父組件</div>
<div class="child">
<!-- v-model給子組件綁定vmodelmsg時,同時默認綁定了input事件給子組件,子組件中使用默認使用value接受屬性,也可以在子組件中使用model選項自定義接受屬性名及事件名 使用$emit觸發事件即可-->
<!-- 此處使用sync綁定tosync屬性的同時,默認綁定了update:tosync事件給子組件,子組件中props中接受tosync屬性即可,使用$emit('update:tosync',xx)觸發事件即可-->
<child1
v-model="vmodelmsg" :tosync.sync ="someMsg" >
</div>
</div>
</template>
<script>
import Child1 from './child1.vue'
export default {
name: 'Communication',
data(){
return {
vmodelmsg:"從父組件傳遞過來的vmodel_msg信息",
someMsg:'從 父組件 .sync方式傳遞過來的信息......'
}
},
components:{
Child1,
},
}
</script>
- 子組件child1.vue
<template>
<div class="child1">
<div>我是child1</div>
<div>
通過v-model方式,從父組件傳遞過來的數據:
{{fromparent_vmodel}}
<br/>
通過sync方式,從父組件傳遞過來的數據:
{{fromsync}}
<br/>
<!-- 子改變父好像不起作用?? -->
<button @click="changfather">vmodel事件改變父組件的值</button>
<button @click="changfathersync">sync改變父組件的值</button>
</div>
</div>
</template>
<script>
export default {
name: 'Child1',
// 爲true時,從父組件中傳入的,此組件沒有通過props接受的參數,都會自動成爲子組件根標籤上的屬性;此時 $attrs中也存在
// 若爲false,則沒有被props接受的參數,都回保存到 $attrs中去
inheritAttrs: false,
// 次數可以自定義接受v-model綁定的屬性及時間名稱
model:{
prop:"vmodel_msg",
event:"vmodelevent"
},
// 接受父組件傳遞過來的數據,並規定接受數據的類型
props:{
vmodel_msg:String,
tosync:String
},
data(){
// 將props中接受到的數據緩存到此處是必要的,因爲你不能直接修改 props 的值
return {
fromparent_vmodel:this.vmodel_msg,
fromsync:this.tosync
}
},
methods:{
changfather(){
this.$emit('vmodelevent','子改變了父中傳過來的屬性')
},
changfathersync(){
this.$emit('update:tosync',"sync......update..666")
}
},
mounted(){
console.log(this,"....this...........");
}
}
</script>
provide 和 inject
provide 選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性
inject 選項應該是:一個字符串數組,或一個對象,對象的 key 是本地的綁定名,value 是注入時的key
在父組件中通過 provider 來提供屬性,然後在子組件中通過 inject 來注入變量。不論子組件有多深,只要調用了 inject 那麼就可以注入在 provider 中提供的數據,而不是侷限於只能從當前父組件的 prop 屬性來獲取數據,只要在父組件的生命週期內,子組件都可以調用。這和 React 中的 Context API 有沒有很相似!
代碼示例
- 父組件communication.vue
<template>
<div class="communication">
<div>我是父組件</div>
<div class="child">
<child1 ></child1>
</div>
</div>
</template>
<script>
import Child1 from './child1.vue'
export default {
name: 'Communication',
data(){
return {
}
},
// 使用provide注入數據
provide:{
providePra:[1,2,3]
},
components:{
Child1,
},
}
</script>
- 子組件child1.vue
<template>
<div class="child1">
<div>我是child1</div>
<div>
<h1>父組件中provide傳遞的:{{fromParentProvide}}</h1>
</div>
</div>
</template>
<script>
export default {
name: 'Child1',
// 此處使用字符串數據或者對象的形式都可以接受到
// inject:['providePra'],
inject:{
'fromParentProvide':'providePra'
},
}
</script>
中央事件總線EventBus
中央事件總線其實就是emit在應用上的全局使用,上面是隻是在局部組件中引入使用,在全局使用測,在跟組件創建之前,先定義中央事件總線:const EventBus = new Vue();然後將中央事件總線賦值到 Vue.prototype 上,即Vue.prototype.EventBus訪問到了,然後使用this.on和this.emit監聽和觸發事件,從而進行各種類型之間的組件通信