ps:本系列博客稍微偏向原理,若只想用法那官方文檔最合適不過了。
知識導航:
- 組件通信
- 插槽
- 總結
1. 組件通信
1.1 父到子
方式一:利用屬性props
此方法的核心是props,那麼先來簡單介紹它。
props的作用接收父組件傳遞過來的數據。它有兩種類型,即數組或對象,而對象是被允許做一些高級配置的,如規定prop的類型、指定初始值、設置爲該prop是否必填、自定義驗證函數。props它的大體實現思路:父組件提供數據,vuejs內部通過子組件的props對數據進行篩選,然後添加到子組件的上下文中。
父組件中:
<Text01 text="測試:方式一01"></Text01>
Text01組件:
模板內:<h2>{{text}}</h2>
js內:props: ["text"],
方式二:引用refs
父組件:
模板內:<Text01 ref="text01Ref"></Text01>
js內:
mounted(){
this.$refs.text01Ref.text02="測試:方式一02",
},
子組件:
模板內:<h2>{{text02}}</h2>
js內:此時的數據不是由props接收的了,可以收在data內。其實是把數據直接給到了此DOM節點上
data() {
return {
text02: "",
};
}
方式三:利用$childen
注意:$children 並不保證順序,也不是響應式的
父組件:
模板內:<Text01></Text01>
js內:
mounted(){
this.$children[0].text03="測試:方式一03"
},
子組件:
模板內: <h2>{{text03}}</h2>
js內:
data() {
return {
text03: ""
};
}
1.2 子到父
子到父一般均採用自定義事件的方式,這裏有一個方法就顯得什麼重要了,即$emit。
$emit的功能是觸發當前實例上的事件,然後把後面的參數給監聽器的回調。
當然這是官方文檔的解釋,什麼意思?
參照vue的源碼簡單解釋一下:事實上所有的事件監聽器的回調函數都會被保存在vm._events中,它的大致格式爲{事件名:回調處理函數}。但 $emit觸發時,會根據 $emit的第一個屬性即事件的名稱去查找對應的鍵值對。找到了則執行它的回調。下面兄弟組件傳參我會寫一個簡單的例子實現。
先看這裏子到父的代碼:
子組件:
模板內: <h2 @click="$emit('text04','子到父')">觸發</h2>
父組件:
模板內<Text01 @text04=handle($event)></Text01>
js內
methods: {
handle(e){
console.log(e);
}
},
通過這個栗子也可以走一下思路,父組件監聽着一個叫做text04的事件。這個東西內會以{text04,fn}這樣的格式保存在一個變量中。子組件中$emit的作用是 1先去那個變量中找鍵叫做text04的鍵值對,找的了,2 把它的回調處理函數fn哪裏把自己參數搞進去即fn(‘子到父’)。fn即handle(e{console.log(e);}。
至於父組件爲毛能監聽到子組件中的事件我沒有去研究vue這塊是怎麼處理的,我把它當成事件冒泡來理解的
1.3 兄弟
1.3.1 如兩兄弟有共同父輩組件
介紹一個通常與$emit配對使用的 $on
$on是監聽當前實例上的自定義事件,事件可以是由 on(event,cb)` 其實還是和上面是差不多的。它的主要目的仍然是將{事件名:fn}保存起來以供$emit使用。cb回調就是事件的處理函數。
在這裏即拿共有祖輩組件派發事件,共有祖輩在監聽事件。幹活的組件始終是一個組件而已,就是我自己觸發了一個事件我自己再收回來
子組件1:
<template>
<div @click="$parent.$emit('text04','兄弟傳參')">
子組件1
</div>
</template>
組件2:
<template>
<div>
<h2>{{text02}}</h2>
</div>
</template>
<script>
export default {
name:'Text02',
data() {
return {
text02: ""
};
},
created() {
this.$parent.$on("text04", msg => {
this.text02 = msg;
});
};
</script>
<script>
export default {
}
};
</script>
1.3.2 無關係的兩個兄弟
首先可以實現一個類,用於事件派發和事件監聽。即實現$emit和 $on.
class Bus {
constructor() {
this.callbacks = {}
//即由於存放{事件名:處理函數}這種格式的對象結構
}
$on(name, fn) {
this.callbacks[name] = fn;
}
$emit(name, args) {
if (this.callbacks[name]) {
this.callbacks[name](args);
}
}
}
//將這個類的實例掛到vue的原型上
Vue.prototype.$bus = new Bus()
組件1:
<h2 @click="$bus.$emit('text05','兄弟傳參之總線模式')">組件1</h2>
組件2:
模板內 <h2>{{text02}}</h2>
js內:
<script>
export default {
name:'Text02',
props: ["text"],
data() {
return {
text02: ""
};
},
created() {
this.$bus.$on('text05',msg=>{
console.log(msg);
this.text02=msg
})
}
};
</script>
1.4 隔代
隔代則又用到了兩個東西:
- provide
- inject
它們一般也是配套使用,主要用於插件和組件庫的開發。它允許一個祖先組件向其所有的子孫後代中注入依賴,且無論組件層次有多深
栗子:
祖先組件:
js內添加一個provide選項,provide選項可以是一個對象,也可以是一個返對象的函數
provide(){
return{
a:"祖孫之間"
}
},
子孫組件:
js內添加一個inject選項,該選項可以是一個字符串數組或對象
inject:['a'],
2. 插槽
插槽相對比較簡單即預留位置
2.1 匿名插槽
// Text01組件
<div>
<slot></slot>
</div>
// 父組件
<Text01>hello</Text01>
2.2 具名插槽
即位置要進行匹配了。
// Text01組件
<div>
<slot></slot>
<slot name="content"></slot>
</div>
//父組件
<Text01>
<template v-slot:content>內容</template>
</Text01>
2.3 作用域插槽
有時候讓插槽內容能夠訪問子組件中的數據是很有必要的。換句話說我們希望在父組件中應用該插槽時能夠獲取去子組件中的信息。
Text03組件:
<slot :foo="foo"></slot>
js內
data() {
return {
foo:'text03組件中的內容'
};
},
父組件:
<Text03>
<!-- 把v-slot的值指定爲作用域上下文對象 -->
<template v-slot:default="ctx">
來自子組件數據:{{ctx.foo}}
</template>
</Text03>