1. 程式人生 > 實用技巧 >Vue元件之間通訊的幾種方式

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>