Vue元件之間通訊的幾種方式
Vue是資料驅動檢視的一個前端框架,所以說對於Vue來說,元件之間的通訊方式非常重要。通訊方式按照元件之間的關係分為父子元件之間通訊,非父子元件之間通訊(兄弟元件,隔代元件)
總結來說:
父子元件通訊: props/$emit; $parent/$children;provide/inject;ref ;$attrs/$listeners
兄弟元件通訊: eventBus
跨級通訊: eventBus;provide/inject;$attrs/$listeners
此外H5中的本地儲存localStorage,sessionStorage可用於元件之間通訊,Vue中的插槽(slot),Vuex也是元件之間通訊的方式,這些在此不多贅述。
1.props和$emit
父元件向子元件傳值:
// 父元件
<template>
<div>
<Childcomp :movies="moviesList"></Childcomp>
</div>
</template>
<script>
import Childcomp from './childcomp.vue'
export default {
components: { Childcomp },
data() {
return {
moviesList: [ '醉拳', '快餐車', '我是誰']
}
}
}
</script>
// 子元件
<template>
<div>
<span v-for="(item, index) in movies" :key="index">{{item}}</span>
</div>
</template>
<script>
export default {
props: ['movies']
}
</script>
props只可以從上一級元件傳遞到下一級元件(父子元件),即所謂的單向資料流。而且 prop 只讀,不可被修改,所有修改都會失效並警告。
子元件向父元件傳值:
// 父元件 <template> <div> <Childcomp @getMoviesList='getMoviesList'></Childcomp>
<span v-for="(item, index) in moviesList" :key="index">{{item}}</span> </div> </template> <script> import Childcomp from './childcomp.vue' export default { components: { Childcomp },
data(){
return{
moviesList:[]
}
}
methods:{
getMoviesList(val){
this.moviesList = val
}
}
} </script>
// 子元件 <template> <div>
<button @click="getMoviesList"></button>
</div> </template> <script> export default { data(){
return(){
moviesList:['簡單任務','警察故事','紅番區']
}
},
methods:{
getMoviesList(){
this.$emit('getMoviesList',{
moviesList
})
}
}
} </script>
2.$parent和$chilren
指定已經建立的父例項,在兩者之間建立父子關係,子例項可以用this.$parent訪問父例項,子例項被推入到父例項的this.$children陣列中。
官方建議:節制的使用此方法,它們是父子通訊的應急方法,推薦用props和events通訊
該方法描述為可以訪問到例項。也就是說可以拿到例項中的data和methods.
// 父元件 <template> <div> <div>父元件的值:{{fatherdata}}</div>
<Childcomp></Childcomp> <button @click="changeChild">點選改變子元件值</button> </div> </template> <script> import Childcomp from './child.vue' export default { components: { Childcomp }, data() { return { fatherdata: '父元件的值' } }, methods: { changeChild() { // 獲取到子元件 this.$children[0].childdata = '子元件的值修改成功' } } } </script>
// 子元件
<template>
<div>
<span>子元件的值為:{{childdata}}</span>
<p>獲取父元件的值為: {{parentVal}}</p>
</div>
</template>
<script>
export default {
data() {
return {
childdata: '子元件中的值'
}
},
computed:{
parentVal(){
return this.$parent.fatherdata;
}
}
}
</script>
$parent和$children的值不一樣,$children 的值是陣列,而$parent是個物件
3.ref和refs
ref用在子元件上,引用指向元件例項,可以通過例項直接呼叫元件的方法或訪問資料。
// 子元件
<template>
<div>
</div>
</template>
<script>
export default {
data() {
return {
childdata: '子元件中的值'
}
},
methods:{
say(){
console.log('子元件中的say方法')
}
}
}
</script>
// 父元件 <template> <div> <Childcomp ref="child"></Childcomp> </div> </template> <script> import Childcomp from './childcomp.vue' export default { components: { Childcomp }, mouted(){ console.log(this.$refs.child.childdata); //
this.$refs.child.say(); //
} } </script>
4.provide和inject
簡單來說就是父元件中通過provide來提供變數, 然後再子元件中通過inject來注入變數。
這裡不論子元件巢狀有多深, 只要呼叫了inject 那麼就可以注入provide中的資料,而不侷限於只能從當前父元件的props屬性中取資料
假如說有三個元件One.vue,Two.vue,Three.vue
// One.vue
<template>
<div>
<Twocomp></Twocomp >
</div>
</template>
<script>
import Twocomp from './Two.vue'
export default {
provide: {
data: "one"
},
components:{
Twocomp
}
}
</script>
// Two.vue <template> <div> <Threecomp></Threecomp>
<span>{{newdata}}</span> </div> </template> <script> import Threecomp from './Three.vue' export default { components:{ Threecomp },
inject:['data'],
data(){
return{
newdata:this.data
}
},
} </script>
// Three.vue
<template>
<div>
<span>{{newdata}}</span>
</div>
</template>
<script>
export default {
inject:['data'],
data(){
return{
newdata:this.data
}
},
}
</script>
5.$attrs和$listeners
隔代元件傳值:
使用props繫結來進行一級一級的資訊傳遞, 如果後代元件中狀態改變需要傳遞資料給父元件, 使用事件系統一級級往上傳遞
使用eventBus,這種情況下還是比較適合使用, 但是碰到多人合作開發時, 程式碼維護性較低, 可讀性也低
使用Vuex來進行資料管理, 但是如果僅僅是傳遞資料, 而不做中間處理,使用Vuex處理感覺有點大材小用了.
為此,引入了$attrs 和$listeners , 新增了inheritAttrs 選項。
包含了父作用域中不作為prop 被識別 (且獲取) 的特性繫結 (class和 style除外)。當一個元件沒有宣告任何 prop 時,這裡會包含所有父作用域的繫結 (class和 style除外),並且可以通過 v-bind="$attrs" 傳入內部元件,在建立高級別的元件時非常有用。
$attrs只代表的是那些沒有被宣告為props的屬性,如果某個prop被子元件中聲明瞭(就是這個屬性已經在子元件的props中了),在子元件中的$attr會把宣告的prop剔除。
// 父元件
<Childcomp class="com" name="attr" :a="a" :b="b" :c="c"></Childcomp>
...
data() {
return {
a: 'Hello a!',
b: 'Hello b!',
c: 'Hello c',
}
},
...
// Childcomp.vue
export default {
components: {
Childcomp2
},
created() {
console.log(this.$attrs) // {name: "attr", a: "Hello a!", b: "Hello b!", c: "Hello c"}
}
}
如果子元件聲明瞭$prop,$attrs中與$props相同的屬性會被移除
// Childcomp.vue
export default {
components: {
Childcomp2
},
props:['a'],
created() {
console.log(this.$attrs) // {name: "attr", b: "Hello b!", c: "Hello c"}
}
}
如果Childcomp裡的子元件還用到foo,可以繼續將foo傳給子元件,同時可以通過 v-bind="$attrs"傳入Childcomp從父元件獲取到的資料。
$listeners是元件的內建屬性,它的值是父元件(不含.native修飾器的) v-on事件監聽器。
元件可以通 過在自己的子元件上使用v-on=”$listeners”,進一步把值傳給自己的子元件。如果子元件已經繫結$listener中同名的監聽器,則兩個監聽器函式會以冒泡的方式先後執行。
6.eventBus
eventBus 又稱為事件匯流排,在vue中可以使用它來作為溝通橋樑的概念, 就像是所有元件共用相同的事件中心,可以向該中心註冊傳送事件或接收事件, 所以元件都可以通知其他元件。
eventBus也有不方便之處, 當專案較大,就容易造成難以維護的災難
使用方法:
1.建立事件匯流排匯出:
import Vue from 'vue'
export const EventBus = new Vue()
在兄弟元件中分別引入EventsBus
2.傳送事件
<template>
<div>
<button @click="add">+</button>
</div>
</template>
<script>
import {EventBus} from './Eventbus.js'
export default {
data(){
return{
num:1
}
},
methods:{
add(){
EventBus.$emit('addnum', {
num:this.num++
})
}
}
}
</script>
3.接收事件
<template>
<div>計算和: {{count}}</div>
</template>
<script>
import { EventBus } from './Eventbus.js'
export default {
data() {
return {
count: 0
}
},
mounted() {
EventBus.$on('addnum', {num} => {
this.count = this.count + num;
})
}
}
</script>