Vue 元件間通訊方法彙總

JS菌發表於2019-04-16

20190416175014.png

Vue 元件間通訊方法彙總

⭐️ 更多前端技術和知識點,搜尋訂閱號 JS 菌 訂閱

除了使用 Vuex 方法外,vue 提供了各種各樣的元件間通訊的方案。文章整理一下父子元件、兄弟元件、祖先後代元件間是如何通訊的。 ?

? 父子元件通訊

props 和 $emit 父子元件通訊

子元件有時需要與父元件進行溝通,溝通的方式就是子元件 emit 事件,父元件通過監聽這個事件來做進一步動作。而父元件與子元件通訊則使用 props

假設這裡有一個父元件並引入了一個子元件 my-comp:

<my-comp v-for="msg in msgs" :key="msg.id" :msg="msg"></my-comp>
複製程式碼

父元件有一系列 msg 資料需要通過子元件渲染,將 msg 作為 prop 傳遞給子元件即可:

import MyComp from '@/components/MyComp.vue'

export default {
  name: 'home',
  components: {
    MyComp
  },
  data () {
    return {
      msgs: [{
        id: 1, data: 'hello js'
      }, {
        id: 2, data: 'css world'
      }, {
        id: 3, data: 'animated style'
      }]
    }
  }
}
複製程式碼

我們通過點選子元件每一項觸發一個事件,父元件監聽這個事件去動態改變子元件的 color 樣式,這就是父元件監聽子元件事件,事件處理函式可以從子元件傳遞值給父元件:

<my-comp v-for="msg in msgs" :key="msg.id" :msg="msg" :colored="colored" @handle-change-color="handleChangeColor"></my-comp>
複製程式碼

首先增加一個事件 handle-change-color 當這個事件被觸發時修改名為 color 的 data,然後將 colored 通過 props 傳入到子元件:

import MyComp from '@/components/MyComp.vue'

export default {
  name: 'home',
  components: { // 註冊元件
    MyComp
  },
  data () {
    return {
      colored: false, // 狀態
      msgs: [{
        id: 1, data: 'hello js'
      }, {
        id: 2, data: 'css world'
      }, {
        id: 3, data: 'animated style'
      }]
    }
  },
  methods: {
    handleChangeColor () {
      this.colored = !this.colored // 監聽事件動態改變 colored
    }
    // handleChangeColor (param) { // 子元件觸發的事件可能包含引數
  }
}
複製程式碼

然後編輯子元件:

<div>
    <div @click="handleClick" :style="{color}">
        {{msg.id}} - {{msg.data}} ⭕
    </div>
</div>
複製程式碼

首先渲染資料,並監聽 click 點選事件,當點選觸發事件處理函式 handleClick

export default {
  name: 'MyComp',
  computed: {
    color () { // color 為樣式
      return this.colored ? 'red' : 'black' // 根據父元件傳入的 props 動態修改樣式
    }
  },
  props: ['msg', 'colored'],
  methods: {
    handleClick (e) {
      this.$emit('handle-change-color') // 使用 $emit 方法觸發父元件 handle-change-color 事件
    //   this.$emit('handler', 'param') // 還可以給事件傳遞引數
    }
  }
}
複製程式碼

子元件接收 colored 父元件傳遞來的 prop,返回一個計算後的屬性 color,根據 colored 返回不同樣式。handleClick 處理當子元件元素被點選時 $emit 派發父元件的 handle-change-color 事件

效果如下:

2019年3月27日08點52分.gif

父元件 $children 操作子元件

使用 $children 操作子元件。如上述例子中,colored 被定義在父元件中,可以將其移動到子元件中,並在父元件通過 $children 訪問到子元件:

<template>
  <div @click="handleClick" class="home">
    <my-comp v-for="msg in msgs" :key="msg.id" :msg="msg"></my-comp>
  </div>
</template>
複製程式碼

handleClick 事件被放置在 div 中

import MyComp from '@/components/MyComp.vue'

export default {
  // ...
  data () {
    return {
      msgs: [{
          // ...
      }]
    }
  },
  methods: {
    handleClick () {
      this.$children.forEach(child => {
        child.$data.colored = !child.$data.colored // 逐一控制子元件的 $data
      })
    }
  }
}
複製程式碼

在子元件中不需要 $emit 事件,只需維護一個 data:

export default {
  name: 'MyComp',
  data () {
    return {
      colored: false // colored 狀態
    }
  },
  computed: {
    color () {
      return this.colored ? 'red' : 'black'
    }
  },
  props: ['msg']
}
複製程式碼

子元件 $parent 訪問父元件

子元件可通過 $parent 來修改父元件的 $data,因此 colored 定義在父元件中。

<template>
  <div class="home">
    <my-comp v-for="msg in msgs" :key="msg.id" :msg="msg" :colored="colored"></my-comp>
  </div>
</template>
複製程式碼

通過 prop 傳遞 colored 引數給子元件

import MyComp from '@/components/MyComp.vue'

export default {
  name: 'home',
  components: {
    MyComp
  },
  data () {
    return {
      colored: false, // 父元件維護一個 colored 狀態
      msgs: [{
          // ...
      }]
    }
  }
}
複製程式碼

父元件定義 colored 狀態

<template>
  <div>
    <div @click="handleClick" :style="{color}">
      {{msg.id}} - {{msg.data}} ⭕
    </div>
  </div>
</template>
複製程式碼

子元件渲染 msg 並監聽 click 事件

export default {
    // ...
  props: ['msg', 'colored'],
  methods: {
    handleClick (e) {
      this.$parent.$data.colored = !this.$parent.$data.colored
    }
  }
}
複製程式碼

通過 $parent 訪問父元件,並修改 $data 狀態

? 非父子元件通訊

中央事件匯流排

我們可以使用使用中央事件匯流排來處理非父子元件間的通訊

具體步驟是建立一個 Vue 例項,然後 $on 監聽事件,$emit 來派發事件

// src/eventBus.js

import Vue from 'vue'
export default new Vue()
複製程式碼

首先建立並匯出一個 Vue 例項

import bus from '@/eventbus'

export default {
    // ...
  methods: {
    handleClick (e) {
      bus.$emit('change-color')
    }
  }
}
複製程式碼

後代元素 $emit 觸發 eventBus 的事件

import bus from '@/eventbus'

export default {
    // ...
  mounted () {
    bus.$on('change-color', () => {
      this.colored = !this.colored
    })
  }
}
複製程式碼

祖先元素 $on 方法監聽 eventBus 的事件

provide/inject

適用於祖先和後代關係的元件間的通訊,祖先元素通過 provide 提供一個值,後代元素則通過 inject 獲取到這個值。這個值預設是非響應的,如果是物件那麼則是響應式的:

export default {
  name: 'home',
  provide () {
    return {
      colored: this.colored // 依賴於 data
    }
  },
  components: {
    MyComp
  },
  data () {
    return {
      colored: { // 必須為物件
        value: false
      },
      msgs: [{
// ... 
複製程式碼

首先通過 provide 對外提供一個 colored,這個屬性依賴於 data 中的 colored,該變數必須為一個物件,才是響應式的。

⚠️ 必須為一個物件

  methods: {
    handleChangeColor () {
      this.colored.value = !this.colored.value
    }
  }
複製程式碼

祖先元件監聽事件或其他途徑去修改 data 改變狀態。

export default {
  name: 'MyComp',
  inject: ['colored'], // inject colored
  computed: {
    color () {
      return this.colored.value ? 'red' : 'black' // do more...
    }
  },
// ...
複製程式碼

後代元件通過 inject 獲取到祖先元件提供的物件,根據物件做進一步動作。

$root 直接訪問根元件

根據官方的文件,我們可以通過 $root 來直接訪問到 Vue 例項

比方說將資料儲存在 Vue 例項中:

// src/main.js

new Vue({
  data () {
    return { // 在這裡!!
      colored: false
    }
  },
  router,
  store,
  render: h => h(App)
}).$mount('#app')
複製程式碼

然後我們在其他各個元件中都能夠使用:

export default {
  name: 'MyComp',
  // ...
  mounted () {
    console.log(this.$root) // 直接訪問到根元件
  },
  // ...
}
複製程式碼

20190416174006.png

JS 菌公眾賬號

請關注我的訂閱號,不定期推送有關 JS 的技術文章,只談技術不談八卦 ?

相關文章