1. 程式人生 > >(十二)vue.js元件——進階篇之元件通訊(3)

(十二)vue.js元件——進階篇之元件通訊(3)

(1)概述

所謂元件間的通訊,實際上就是指在各個元件間,進行引數或者資訊的相互傳遞。比如我們前面學的通過props給子元件傳參,實際上這就是父元件向子元件進行單向的通訊。

(2)元件間通訊的幾種方式

1.父到子的通訊

父到子的通訊使用我們前面使用的props即可。

2.子到父的通訊
父子元件的概念
父元件:主動發起呼叫的哪一方。父元件一般可以給子元件傳參
子元件:被呼叫的哪一方。子元件可以接受父元件傳來的引數

例如:
當我們在一個vue例項繫結的div中使用自定義元件component1,那麼
這個vue例項就是父元件,被呼叫的component1就是子元件。

(3)子元件如何向父元件通訊

當子元件需要向父元件通訊時,原理與流程類似於下面的描述:
1.在父元件中定義一個可以接受子元件資訊的方法
2.然後把這個方法類似於傳參一樣傳入子元件,(v-on+自定義事件名繫結)
3.在子元件就獲取此方法,並進行傳參呼叫來修改父元件內容。this.$emit(事件名,引數)

其實說白了,真正執行修改父元件的資料的程式碼還是定義在父元件的方法中的,只是由子元件觸發並呼叫了而已。

在下圖中我定義了一個自定義的事件:my-handle,靠他來實現子向父的傳參
在這裡插入圖片描述
初始化執行結果:
在這裡插入圖片描述
點選子元件中的按鈕後
在這裡插入圖片描述
**關於emit[;;;]vue:調.調this.emit方法** 其中文含義: [發出;發射;頒佈;發表], 在vue中作用: 用於呼叫觸發父元件傳遞的自定義事件. 呼叫形式: this.

.調this.emit(自定義事件名,引數(可選))

(4)擴充套件一:在元件標籤上繫結原生事件

1.在元件標籤標籤上通過v-on繫結事件,如果未做宣告那麼繫結的就會是自定義事件,哪怕和原生事件名稱一樣。
如果要在在元件上監聽原生DOM事件,可使用修飾符:.native對事件進行修飾,這樣就可繫結原生DOM事件。
以click事件為例,在元件上繫結原生click事件,繫結就是包裹元件所有內容的最外層根元素,即click是繫結在該元件的最外層元素上的。
在這裡插入圖片描述
雖然這個很少用,但是不得不說在某些時候特別有用。

(5)擴充套件二:自定義事件@input的語法糖—》v-model

概述


當我們使用input事件作為事件名時,只要沒有加上,native修飾符,那麼就會作為自定義事件處理。
還是剛才的程式碼,我們只是把自定義事件的名稱由 my-handle 變為了 input ,然後執行,結果和剛才的一樣。
在這裡插入圖片描述
然後這裡就是我要說的了,當我們使用@input作為自定義事件名時,我們其實可以使用語法糖來簡化程式碼

簡化點:
1.可以去掉父元件中函式
2.在子元件標籤上繫結時,可以使用v-model繫結,由於去掉了函式只需要繫結要改變的那個值就可以了。
當然,雖然我們使用的是v-model指令,但是這是@input事件的語法糖寫法,所以我們還是需要在$emit方法裡面寫input事件名。
在這裡插入圖片描述

<my-counter v-model="mes"></my-counter>
等價於
<my-counter @input="mesv()"></my-counter>
methods:{
mesv:function(v){
this.mes=v;
}
}

直接用語法糖v-model繫結一個計算屬性

上面我們在v-model繫結的直接就是一個簡單的例項變數,其實我們也可以直接繫結一個計算屬性。這樣可以應對一些比較複雜的情況。
在這裡插入圖片描述

v-model與表單子元件-----雙向繫結
原理:在子元件的input事件上繫結,監聽子元件事件的變化,變了就馬上向父元件傳值…
當只有一個輸入框時,直接使用v-model繫結
在這裡插入圖片描述

當是一個完整的表單,有多個輸入框時,使用@input繫結
Html程式碼
在這裡插入圖片描述
登入元件程式碼
在這裡插入圖片描述
提示:也可以直接使用一個物件與表單繫結,實現雙向繫結效果。

(6)任意元件間的通訊

在vue1中的通訊方式(已經被廢棄)
對於【祖先和後代】元件的通訊,採取的是dispatch()dispatch()方法和broadcast()方法實現。

$dispatch()方法: 用於向祖先級級派發事件
$broadcast()方法:用於向子孫級派發事件

然後接受事件方元件只要新增一個選項:events(他和data同級),這樣在這個選項裡面就可以收到此事件資訊了。
然後這兩個方法發出後,在那個方向線上(祖先至後代)的任何元件都可以接受到,不過事件本身會按照就近原則傳遞,且會在第一次接收時停止冒泡,除非返回true.

<!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>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<style>
/* #app * {
border: 1px solid red;
margin: 10px;
} */
</style>
</head>
<body>
<div id="app">
<button @click="tosub">向子元件傳值</button>{{mes}}
<br><br>
<my-counter></my-counter>
</div>
<script>
//全域性組成的元件
Vue.component("my-counter", {
template: `<div><button @click="toParent"> 向父元件傳值 </button>{{submes}}</div>`,
data: function () {
return {
submes: "我是子元件的內容",
}
},
methods: {
toParent: function () {
this.$dispatch("my-handle","我是來自子元件的資訊--------------my-counter");
}
},
events: {
"par-mes": function (message) {
this.submes = message;
},
}
});
//vue例項
let vueApp = new Vue({
el: "#app",
data: {
mes: "我是父元件的內容",
},
methods: {
tosub: function () {
this.$broadcast("par-mes", "我是來自父元件的資訊--------666666666666");
}
},
events: {
"my-handle": function (message) {
this.mes = message;
},
}
});
</script>
</body>
</html>

缺點: 這兩個方法雖然看起來很好用,但是缺點太明顯了。
1.他不能解決兄弟間元件的通訊
2.在元件結構擴充套件的過程中,這種方式的可用性會變得越來越脆弱。

所以,在vue2中這種方式被廢棄了!

在vue2中的通訊方式(主流用法)
在vue2中,推薦使用一個空的vue例項作為中央事件匯流排,來負責所有的資訊轉發。類似於中介。(其實也就是設計者模式的觀察者模式)
這種方式要求如下:
1.建立一個作為中介的vue例項。 var bus=new Vue();
2.所有元件都把要發的資訊發給中介例項.

bus.$emit(事件名,引數)

3.所有相關元件監聽中介例項是否接收到了資訊,好及時獲取。

    bus.$on(事件名,回撥函式)

由於中介例項能被任何元件訪問,所以也就實現了任意元件間的通訊。
定義一個空的中央事件匯流排例項(類似於中介)

var bus = new Vue();

定義一個全域性元件
在這裡插入圖片描述
建立一個vue例項,作為父元件
在這裡插入圖片描述
父元件所繫結的網頁模板程式碼
在這裡插入圖片描述
提示:

其他兄弟元件、跨多級的任意元件,通訊方式都是如此。
1.需要發信息的元件,使用bus.$emit()方法向中央事件匯流排傳值就可以了
2.需要收訊息的元件,使用bus.$on()方法向中央事件匯流排物件取值就可以了
3.在元件向中央事件匯流排監聽是否有收到值時,應該把監聽寫在mounted生命週期函式裡面,好確保在元件渲染完成後才開啟監聽的,避免出現異常。
4.這種方法實現了任何元件間的通訊,如果深入使用,可以擴充套件bus例項,給它新增data、computed、methods等選項,這些都是可以公用的,在業務中,尤其是協同開發時非常有用,因為經常需要共享一下通用資訊,比如使用者名稱,token等。對於這些資訊只要在初始化時讓bus獲取一次,任何時間,任何元件都可以直接使用了(常用語spa單頁應用模式)

(7)擴充套件----父鏈和子鏈

父鏈和子鏈是一種父子間元件通訊的新方式。
概念
父鏈:
在子元件中,使用this.$parent可以直接訪問該元件的父例項或元件,【類似於原生或者jquery中獲取父節點的方式】

子元件:
在父元件中,使用this.$children訪問它所有的子元件,當子元件較多時可以通過子元件索引名稱來實現查詢。【類似於原生或者jquery中獲取子節點的方式】
說明

1.this.$parent與this.$children可以無限的向上或向下訪問,直到根例項或最內層元件。
2.然後由於子元件可以有多個,this.$children獲取子元件時,獲取的的是一個數組,要給上下標才能獲取到具體的某一個子元件。
3.這時操作起來不是很方便,我們就可以使用:子元件索引的方式實現獲取具體子元件

父鏈例項
在這裡插入圖片描述
子鏈例項
在這裡插入圖片描述
子元件索引例項
給子元件定義索引時,需要在子元件呼叫標籤上使用ref屬性定義,如:
<my-btn ref="sub2"></my-btn>

然後在父元件中獲取時,通過this.$refs.refName的方式獲取具體的子元件,然後.$refs只在元件渲染完成後才會充填。

this.$refs.sub2.submes = "來自父元件的資訊--sub2";

在這裡插入圖片描述

由於$refs是非響應式的,所以他僅僅作為直接訪問子元件的一個應急方案。應當避免在計算屬性或者模板中使用$refs.

父鏈和子鏈總結

1.父鏈與子鏈的父子元件通訊方式非常相似與傳統的dom節點通訊方式,當然他仍然沒有去操作dom節點,他操作的仍然是資料。
2.不過這種方式會讓父子元件間出現 緊耦合 子看父元件或子看子元件很難他們的狀態,因為他隨時可能會被任意元件修改,所以這種方式並不推薦!
3.父子元件通訊最好還是使用props與【自定義事件+$emit】的方式來通訊。
4.跨級元件間的通訊還是使用中央事件匯流排的方式實現通訊。【bus.$emit(),bus.$on()】