vue中非父子組件的通信你瞭解嗎

我們總結瞭父子組件通信方式有:props + emit。這裡我們將總結一下,非父子組件通信方式,這裡還不涉及到Vuex。

如果存在祖孫組件,我們可以通過ProvideInject進行通信。

如果不是祖孫組件,也不是父子組件,我們可以采用Mitt全局事件總線第三方庫來實現。

一、Provide和Inject

ProvideInject用於非父子組件之間共享數據,比如有一些深度嵌套的組件,子組件想要獲取父組件的數據,如果不存在Provide和Inject選項,我們可以通過props進行一次傳遞數據,但是這樣做太過於繁瑣。

對於上述情況,我們可以使用ProvideInject無論組件結構嵌套有多深,父組件都可以作為子組件數據的提供者。

父組件存在Provide來提供數據。

子組件存在Inject來獲取數據。

在實際過程中,父組件不知道哪個子組件使用其數據,子組件也不知道使用的是哪個父組件的數據。

Provide和Inject的基本使用

//父組件
<template>
  <div>
    <Content></Content>
  </div>
</template>
<script>
import Content from "./components/Content.vue";
export default {
  data() {
    return {};
  },
  provide:{
    name:"張三",
    age:20
  },
  components: {
    Content
  }
};
</script>
<style scoped></style>
//子組件
<template>
  <div>
    <h1>這裡是Content組件</h1>
    <h1>{{ name }} -- {{age}}</h1>
    <ContentBase></ContentBase>
  </div>
</template>
<script>
import ContentBase from "./ContentBase.vue";
export default {
  data() {
    return {};
  },
  components: {
    ContentBase
  },
  inject:["name", "age"]
};
</script>
<style scoped></style>
//孫組件
<template>
  <div>
      <h1>這裡是contentBase組件</h1>
      <h1>{{name}} -- {{age}}</h1>
  </div>
</template>
<script>
export default {
  data() {
    return {};
  },
  inject: ["name", "age"]
};
</script>
<style scoped>
</style>

最終顯示結果為:

在這裡插入圖片描述

二、Provide和Inject的另一種寫法

我們思考一種情況,如果Provide拿到的數據,是從data拿到的數據,此時如果獲取? 此時this可以使用嗎?

<template>
  <div>
    <Content></Content>
  </div>
</template>
<script>
import Content from "./components/Content.vue";
export default {
  data() {
    return {
      source: ["111","222", "333"]
    };
  },
  provide:{
    name:"張三",
    age:20,
    res: this.source.length   //我們在此時增加res,想要通過this.source.length拿到數組的長度
  },
  components: {
    Content
  }
};
</script>
<style scoped></style>

該結果是錯誤的。

在這裡插入圖片描述

報錯信息顯示,我們從undefined上讀取屬性,此時this為undefined。原因:從上面代碼我們可以看出,this指向的是undefiend(因為this執行最外層)。

解決方法

我們將Provide設置為一個函數,並且返回一個對象,如下代碼所示:

<template>
  <div>
    <Content></Content>
  </div>
</template>
<script>
import Content from "./components/Content.vue";
export default {
  data() {
    return {
      source: ["111", "222", "333"],
    };
  },
  provide() {
    return {
      name: "張三",
      age: 20,
      res: this.source.length,
    };
  },
  components: {
    Content,
  },
};
</script>
<style scoped></style>

顯示結果:

在這裡插入圖片描述

此時我們再思考一個問題,如果我們向data數組中新增一個元素,在其他地方獲取的數組長度會跟隨變化嗎?

<template>
  <div>
    <Content></Content>
    <button @click="addOneItem">點擊</button>
  </div>
</template>
<script>
import Content from "./components/Content.vue";
export default {
  data() {
    return {
      source: ["111", "222", "333"],
    };
  },
  provide() {
    return {
      name: "張三",
      age: 20,
      res: this.source.length,
    };
  },
  components: {
    Content,
  },
  methods:{  //在這裡添加點擊事件
    addOneItem() {
      this.source.push("nnn")
      console.log(this.source)
    }
  }
};
</script>
<style scoped></style>

在這裡插入圖片描述

在這裡插入圖片描述

結果如上圖所示,可以看到數據是被添加進去的,但是子組件並沒有檢測到數據的變化。

此時我們可以使用computed來檢測this.source.length的變化,使用代碼如下所示。

<template>
  <div>
    <Content></Content>
    <button @click="addOneItem">點擊</button>
  </div>
</template>
<script>
import Content from "./components/Content.vue";
import { computed } from "vue"   //從vue中引入computed
export default {
  data() {
    return {
      source: ["111", "222", "333"],
    };
  },
  provide() {
    return {
      name: "張三",
      age: 20,
      res: computed(() => this.source.length), //在這裡添加computed
    };
  },
  components: {
    Content,
  },
  methods:{
    addOneItem() {
      this.source.push("nnn")
      console.log(this.source)
    }
  }
};
</script>
<style scoped></style>

在這裡插入圖片描述

在這裡插入圖片描述

因為我們通過computed獲取的是一個對象,此時我們通過value屬性拿到值。

三、全局事件總線mitt庫

在vue2時,如果我們使用事件總線可以使用this.$bus = new Vue()也就是實例化一個vue對象。但是我們在vue3中不能這樣用。所以我們采用第三方庫來實現組件之間的通信。這個第三方庫為mitt

一、安裝

npm install mitt

在文件中引入並且進行初始化導出。

import mitt from "mitt";
const emitter = new mitt()
export default emitter

監聽事件,第一個參數是事件名,第二個參數是回調函數。

    emitter.on("why", (data) => {
      console.log(data)
    })
    //*表示可以監聽全部的事件。
    emitter.on("*", (type, data) => {
      console.log(type, data)
    })

在這裡插入圖片描述

取消事件

//取消emitter中所有的監聽
emitter.all.clear()
//或者
//定義一個函數
function onFoo(){}
emitter.on("foo", onFoo)
emitter.on("foo", onFoo)

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!   

推薦閱讀: