12.組件化開發(fā)2-非父子組件之間通信-祖先和后代之間的通信

在開發(fā)中原茅,我們構(gòu)建了組件樹之后,除了父子組件之間的通信之外抡医,還會有非父子組件之間的通信
非父子組件的通信又可以分為祖先組件和后代組件之間的通信早敬、兄弟組件之間的通信忌傻、沒有任何關(guān)系組件之間的通信
這里我們主要講祖先組件和后代組件之間的通信

祖先組件向后代組件傳遞數(shù)據(jù):

祖先組件通過provide向后代組件傳遞數(shù)據(jù)
后代組件通過inject接收祖先組件傳遞給后代組件的數(shù)據(jù)

image.png

project/inject用法

image.png

基本用法

對象寫法:祖先組件只能傳給子孫一些固定的數(shù)據(jù)大脉,不能把data里的數(shù)據(jù)傳給子孫組件
App.vue

<template>
    <div>
        <Home></Home>
    </div>
</template>

<script>
    import Home from './Home.vue'
    export default {
        components: {
            Home,
        },
        provide: {//對象寫法只能傳給子孫一些固定的數(shù)據(jù),不能把data里的數(shù)據(jù)傳給子孫組件
             name: 'why',
             age: 18
        },
        data() {
            return {
                message: '今天天氣不錯'
            }
        }
    }
</script>

App.vue的子組件Home.vue

<template>
    <div>
        <home-content></home-content>
    </div>
</template>

<script>
    import HomeContent from './HomeContent.vue'
    export default {
        components: {
            HomeContent
        }
    }
</script>

App.vue的孫子組件HomeContent.vue

<template>
    <div>
        姓名:{{name}} - 年齡: {{age}}
    </div>
</template>

<script>
    export default {
        inject: ['name', 'age']
    }
</script>

進階用法

如果想把祖先組件data中的數(shù)據(jù)傳遞給后代組件水孩,需要把provide寫成函數(shù)形式镰矿,此函數(shù)返回一個對象
App.vue

<template>
    <div>
        <input v-model="message" />
        <Home></Home>
    </div>
</template>

<script>
    import Home from './Home.vue'
    export default {
        components: {
            Home,
        },
        provide() {
            //函數(shù)寫法可把data中的數(shù)據(jù)傳遞給子孫組件
            return {
                name: 'why',
                age: 18,
                message: this.message
            }
        },
        data() {
            return {
                message: '今天天氣不錯'
            }
        }
    }
</script>

App.vue的子組件Home.vue

<template>
    <div>
        <home-content></home-content>
    </div>
</template>

<script>
    import HomeContent from './HomeContent.vue'
    export default {
        components: {
            HomeContent
        }
    }
</script>

App.vue的孫子組件HomeContent.vue

<template>
    <div>
        姓名:{{name}} - 年齡: {{age}}
        <p>{{message}}</p>
    </div>
</template>

<script>
    export default {
        inject: ['name', 'age', 'message']
    }
</script>

執(zhí)行npm run serve運行項目,并在瀏覽器打開

image.png

修改祖先組件App.vue中input輸入框的值荷愕,改變message的值
后代組加HomeContent.vue接收到的message依然為祖先組件App.vue中data里message屬性的初始值衡怀,
修改祖先元素中message的值,后代組件接收到的message的值并不會響應(yīng)式改變
image.png

深度進階用法

如果希望祖先組件傳遞給后代組件數(shù)據(jù)安疗,如果祖先元素中數(shù)據(jù)發(fā)生改變抛杨,后代組件接收到的數(shù)據(jù)也響應(yīng)式發(fā)生改變,需要借助vue3提供的computed Api
App.vue

<template>
    <div>
        <input v-model="message" />
        <Home></Home>
    </div>
</template>

<script>
    import Home from './Home.vue'
    import { computed } from "vue"
    export default {
        components: {
            Home,
        },
        provide() {
            //使用computed把message的值變成一個響應(yīng)式對象,computed函數(shù)的返回值為ref對象
            return {
                name: 'why',
                age: 18,
                message: computed(() => this.message),
            }
        },
        data() {
            return {
                message: '今天天氣不錯'
            }
        }
    }
</script>

App.vue的子組件Home.vue

<template>
    <div>
        <home-content></home-content>
    </div>
</template>

<script>
    import HomeContent from './HomeContent.vue'
    export default {
        components: {
            HomeContent
        }
    }
</script>

App.vue的孫子組件HomeContent.vue

<template>
    <div>
        姓名:{{name}} - 年齡: {{age}}
        <!--message為ref對象荐类,值存儲在value中-->
        <p>{{message.value}}</p>
    </div>
</template>

<script>
    export default {
        inject: ['name', 'age', 'message']
    }
</script>

執(zhí)行npm run serve運行項目怖现,并在瀏覽器打開
修改祖先元素中message的值,后代組件接收到的message的值也會響應(yīng)式改變

image.png

如果希望祖先組件傳遞給后代組件的數(shù)據(jù)玉罐,如果祖先元素中數(shù)據(jù)發(fā)生改變屈嗤,后代組件接收到的數(shù)據(jù)響應(yīng)式發(fā)生改變,但是不用vue3api吊输,只用vue2api

App.vue

<template>
    <div>
        <input v-model="message" />
        <Home></Home>
    </div>
</template>

<script>
    import Home from './Home.vue'
    export default {
        components: {
            Home,
        },
        provide() {
            //使用vue2用法的話饶号,message的值改為一個有返回值的方法
            return {
                name: 'why',
                age: 18,
                message: () => this.message,
            }
        },
        data() {
            return {
                message: '今天天氣不錯'
            }
        }
    }
</script>

App.vue的孫子組件HomeContent.vue

<template>
    <div>
        姓名:{{name}} - 年齡: {{age}}
        <!--message為ref對象,值存儲在value中-->
        <p>{{message()}}</p>
    </div>
</template>

<script>
    export default {
        inject: ['name', 'age', 'message']
    }
</script>

后代組件向祖先組件傳遞數(shù)據(jù):

通過使用mitt模塊創(chuàng)建的事件總線emitter實現(xiàn)
后代組件通過emitter.emit發(fā)射事件,可以攜帶參數(shù)
祖先組件通過emitter.on監(jiān)聽事件季蚂,在回調(diào)中接收參數(shù)
例:想要實現(xiàn)點擊HomeContent中的修改message按鈕茫船,修改App.vue中的message的值

1.安裝mitt
npm install mitt --save
2.封裝src/utils/eventbus.js
import mitt from "mitt"
const emitter = mitt()
export default emitter
3.App.vue中通過emitter.on監(jiān)聽其他組件中通過事件總線發(fā)射的具體某個事件
<template>
  <div>
    <input v-model="message" />
    <Home></Home>
  </div>
</template>

<script>
import Home from "./Home.vue";
import { computed } from "vue";
import emitter from "./utils/eventBus";
export default {
  components: {
    Home,
  },
  provide() {
    return {
      name: "why",
      age: 18,
      message: computed(() => this.message),
    };
  },
  created() {
    //監(jiān)聽事件
    emitter.on("changeMes", (message) => {
      console.log(message);
      this.message = message;
    });
  },
  data() {
    return {
      message: "今天天氣不錯",
    };
  },
};
</script>

App.vue的子組件Home.vue

<template>
    <div>
        <home-content></home-content>
    </div>
</template>

<script>
    import HomeContent from './HomeContent.vue'
    export default {
        components: {
            HomeContent
        }
    }
</script>
4.App.vue的孫子組件HomeContent.vue中通過emitter.emit發(fā)射事件
<template>
    <div>
        姓名:{{name}} - 年齡: {{age}}
        <!--message為ref對象,值存儲在value中-->
        <p>{{message.value}}</p>
        <button @click="changeMessage">修改message</button>
    </div>
</template>

<script>
import emitter from "./utils/eventBus";
export default {
  inject: ["name", "age", "message"],
  methods: {
    changeMessage() {
      //發(fā)射事件
      emitter.emit("changeMes", "雷電預(yù)警");
    },
  },
};
</script>

執(zhí)行npm run serve扭屁,效果如下圖算谈,點擊了修改message按鈕,App.vue中message被改變


image.png

image.png
5.如果想在App.vue中監(jiān)聽所有組件通過事件總線發(fā)射的事件料滥,可以通過下面:
//監(jiān)聽所有組件通過事件總線發(fā)送的事件
    emitter.on("*", (type, message) => {
      console.log("第一個參數(shù)為監(jiān)聽到的事件類型", type);
      console.log("第二個參數(shù)為監(jiān)聽到的事件傳遞的參數(shù)", message);
    });
例:
<template>
  <div>
    <input v-model="message" />
    <Home></Home>
  </div>
</template>

<script>
import Home from "./Home.vue";
import { computed } from "vue";
import emitter from "./utils/eventBus";
export default {
  components: {
    Home,
  },
  provide() {
    return {
      name: "why",
      age: 18,
      message: computed(() => this.message),
    };
  },
  created() {
    //監(jiān)聽事件
    emitter.on("changeMes", (message) => {
      console.log(message);
      this.message = message;
    });
    //監(jiān)聽所有組件通過事件總線發(fā)送的事件
    emitter.on("*", (type, message) => {
      console.log("第一個參數(shù)為監(jiān)聽到的事件類型", type);
      console.log("第二個參數(shù)為監(jiān)聽到的事件傳遞的消息", message);
    });
  },
  data() {
    return {
      message: "今天天氣不錯",
    };
  },
};
</script>

<style scoped></style>

image.png
6.Mitt的事件取消

在某些情況下我們可能希望取消掉之前注冊的函數(shù)監(jiān)聽:

6.1取消emitter中所有的監(jiān)聽
emitter.all.clear()
6.2取消具體某個事件的監(jiān)聽
//監(jiān)聽的時候然眼,需要把觸發(fā)監(jiān)聽調(diào)用的回調(diào)函數(shù)的引用地址進行保存
const onFoo = () => {}
emitter.on('foo', onFoo) //監(jiān)聽
emitter.off('foo', onFoo) //取消監(jiān)聽

mitt模塊創(chuàng)建的事件總線不僅可以用在后代組件向祖先組件發(fā)送事件傳遞消息,祖先組件監(jiān)聽事件葵腹,
也可以用在任何兩個組件之間高每,其中一個向另一個發(fā)射事件,傳遞消息

此文檔主要內(nèi)容來源于王紅元老師的vue3+ts視頻教程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末践宴,一起剝皮案震驚了整個濱河市觉义,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浴井,老刑警劉巖晒骇,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡洪囤,警方通過查閱死者的電腦和手機徒坡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘤缩,“玉大人喇完,你說我怎么就攤上這事“。” “怎么了锦溪?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長府怯。 經(jīng)常有香客問我刻诊,道長,這世上最難降的妖魔是什么牺丙? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任则涯,我火速辦了婚禮,結(jié)果婚禮上冲簿,老公的妹妹穿的比我還像新娘粟判。我一直安慰自己,他們只是感情好峦剔,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布档礁。 她就那樣靜靜地躺著,像睡著了一般吝沫。 火紅的嫁衣襯著肌膚如雪呻澜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天野舶,我揣著相機與錄音,去河邊找鬼宰衙。 笑死平道,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的供炼。 我是一名探鬼主播一屋,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼袋哼!你這毒婦竟也來了冀墨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤涛贯,失蹤者是張志新(化名)和其女友劉穎诽嘉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡虫腋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年骄酗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悦冀。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡趋翻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盒蟆,到底是詐尸還是另有隱情踏烙,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布历等,位于F島的核電站讨惩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏募闲。R本人自食惡果不足惜步脓,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浩螺。 院中可真熱鬧靴患,春花似錦、人聲如沸要出。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽患蹂。三九已至或颊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間传于,已是汗流浹背囱挑。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沼溜,地道東北人平挑。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像系草,于是被迫代替她去往敵國和親通熄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容