Vue組件化, 組件間通信方式
1.0 父子組件之間通信
1.1 父傳子
1.1.1 props
方式傳參.
props 通信的方式是 父級組件和子級組件之間通信的一種方式, 具體使用方法如下
- 子組件接收數據時的方式有 props: [’’] 數組的方式**
- 或者通過 props:{} 對象的方式接收數據
🏴 注意: 子組件中的函數或方法如果要操作父組件的數據的時候,通常是在父組件定義一個方法, 把這個方法通過標籤屬性props傳給子組件, 在子組件中調用該方法把參數傳遞過去,完成子組件對父組件數據的操作
案例代碼如下:
Parent 父組件:
<template> <div> <h1 style="color: pink">我是parent組件, 下面是Son組件</h1> <Son msg="我是props 方式給子組件傳參"/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son, }, methods: { }, } </script> <style scoped> </style>
children子組件:
<template> <div> <h3>我是son組件: {{msg}}</h3> </div> </template> <script> export default { name: "Son", props: ['msg'], mounted() { console.log(this.msg); } } </script> <style scoped> </style>
1.1.2 自定義函數傳參
自定義函數的方式, 可以實現父組件的函數在子組件中觸發, 這種方式可以在子組件中, 再調用函數的是候, 給父組件傳遞數據
// 1. 在 Vue中也可以通過自定義函數的方式和子組件之間通信, 一般會在自定義函數的函數名字之前添加一個 @ 符號, 寫法如下. <!-- 父組件自定義一個函數, 通過標籤屬性傳遞給子組件 --> <Son @selfDefineEvent="selfDefineEvent"/> // 2. 這種方式, 在子組件中, 可以直接通過 this.$emit('自定義函數名', 參數) 方法分發事件. (相當於觸發傳遞過來的事件) /** * 自定義事件通信方式的方式 * 1. 在父組件中定義一個事件函數, 通過標籤屬性的方式把該自定義事件傳給子組件 * 2. 在子組件中直接通過 this.$emit() 來觸發事件. * 3. 其本質是在子組件調用該自定義事件的時候, 通過傳遞參數的方式, 把數據傳遞給父組件 */
案例代碼如下:
Parent父組件:
<template> <div> <h1 style="color: pink">我是parent組件, 下面是Son組件</h1> <!-- 父組件自定義一個函數 --> <Son @selfDefineEvent="selfDefineEvent"/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son, }, methods: { selfDefineEvent(...params) { console.log(...params); } }, } </script> <style scoped> </style>
Son 子組件代碼:
<template> <div> <h3>我是son組件</h3> <button @click="handleClick">Son 組件內點擊觸發父組件 自定義函數</button> </div> </template> <script> export default { name: "Son", methods: { handleClick() { // 在子組件內, 直接通過 $emit 觸發父組件自定義的函數 this.$emit('selfDefineEvent', '我是自定義函數參數',123,'mm') } }, } </script> <style scoped> </style>
原理:
1. // 在vue中, 每一個組件, 包括 App 組件, 如果你在組件的聲明週期函數中 打印 console.log(this), 會打印出是一個 VueComponent{} 對象, 代表當前的組件實例對象 2. // 而 在 vue 中, 所有的組件對象, 包括 App 等其他一些子組件的實例 都是 Vue 這個組件的實例對象的子對象, 他們之間存在繼承的關係. 3. // 在 vue中, 通過 @事件名, 自定義的事件, 都會放在 Vue 這個組件的 prototype 中, 所以 Vue組件的實例 vm對象及其所有 子組件實例對象,都可以找到 這個自定義事件. 所以就可以在它的子組件中通過 原型鏈就可以找到 Vue 組件對象的 $emit 這個方法, 完成通信. this.$emit('自定義事件名字', 參數);
1.2 子傳父
vue 組件之間通信之 - 子組件傳遞數據給父組件.
即: 父組件訪問子組件的數據.
1.2.1 ref
實現: 父組件訪問子組件數據
通過 ref 屬性給子組件做個標識, 可以獲取到子組件, 進而可以獲取到子組件
data
中的數據, 也可以獲取到子組件當中的相關方法.代碼示例如下:
Parent組件
<template> <div> <h1 style="color: pink">我是parent組件, 下面是Son組件</h1> <Son ref="sonRef" msg="我是父組件傳遞給子組件的msg屬性..."/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son }, mounted() { console.log('父組件想要獲取子組件數據---->> ',this.$refs.sonRef.sonData); // 獲取子組件 data內的數據 console.log('父組件想要獲取子組件數據---->> ',this.$refs.sonRef.msg); // 獲取子組件 pros 內的數據 this.$refs.sonRef.sayHi() // 調用子組件的方法 } } </script> <style scoped> </style>
Son 組件
<template> <h3>我是son組件</h3> </template> <script> export default { name: "Son", props:['msg'], data() { return { sonData: '我是子組件的數據' } }, methods: { sayHi() { console.log('%c hello parents, I am your son ...', 'color:pink'); } } } </script> <style scoped> </style>
1.2.2 $children
實現: 父組件訪問子組件數據
- 類型: Array<Vue instance> - 只讀 - 詳細: 當前實例的 "直接" 子組件. 需要注意的是, `$children`"並不保證順序, 也不是響應式的.",如果你發現自己正在嘗試使用 `$children` 來進行數據綁定, 考慮使用一個數組配合 `v-for` 來生成子組件, 並且使用 Array 作爲真正的來源.
通過
$children
的方式獲取到當前組件的所有子組件, 用索引獲取指定子組件, 然後獲取到對應子組件的props
,data
以及method
方法等但是: 使用
$children
的這種方式,vue
中不一定是按照順序的,vue
中通過$children
獲取到的子組件是不保證這裏取到子組件的順序的.
Parent組件代碼如下
<template> <div> <h1 style="color: pink">我是parent組件, 下面是Son組件</h1> <Son ref="sonRef" msg="我是父組件傳遞給子組件的msg屬性..."/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son }, mounted() { console.log(this.$children[0].msg); // 訪問子組件 props 屬性 console.log(this.$children[0].sonData); // 訪問子組件 data this.$children[0].sayHi() // 訪問子組件 method 方法 } } </script> <style scoped> </style><template> <div> <h1 style="color: pink">我是parent組件, 下面是Son組件</h1> <Son ref="sonRef" msg="我是父組件傳遞給子組件的msg屬性..."/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son }, mounted() { console.log(this.$children[0].msg); // 訪問子組件 props 屬性 console.log(this.$children[0].sonData); // 訪問子組件 data this.$children[0].sayHi() // 訪問子組件 method 方法 } } </script> <style scoped> </style>
子組件代碼同上面子組件的代碼
1.2.3 $emit
事件派發與監聽
注意: 事件派發與監聽, 雖然能在父組件中獲取到子組件派發事件所傳遞的數據, 但是實際上, 還是在 子組件上監聽的事件. 即:
誰派發事件, 誰來監聽事件
.Parent 組件如下
<template> <div> <h1 style="color: pink">我是parent組件, 下面是Son組件</h1> <!-- 監聽子組件派發的事件 --> <Son @eventEmit="handleEventEmit" ref="sonRef" msg="我是父組件傳遞給子組件的msg屬性..."/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son }, mounted() { }, methods: { handleEventEmit(params) { console.log('父組件監聽到子組件派發的數據 -->', params); } } } </script> <style scoped> </style>
Children組件如下
<template> <div> <!-- 子組件綁定派發事件 --> <h3 @click="$emit('eventEmit','我是事件派發想要傳遞的數據123.')">我是son組件</h3> </div> </template> <script> export default { name: "Son", props: ['msg'], data() { return { sonData: '我是子組件data 的數據...' } }, methods: { sayHi() { console.log('%c hello parents, I am your son ...', 'color:pink'); } } } </script> <style scoped> </style>
2.0 兄弟組件之間的通信
2.1 $emit
和 $on
, 通過共同父組件實現兄弟組件通信
藉助共有父組件的方式來實現.
用事件的派發與監聽, 通過共同父組件來派發和監聽事件, 實現兄弟組件之間通信
下面案例實現
Son
組件與Son1
組件間通信, 共同父組件Parent
Parent 組件如下
<template> <div> <h1 style="color: pink">我是parent組件, 下面是Son組件</h1> <!-- 監聽子組件派發的事件 --> <Son @eventEmit="handleEventEmit" ref="sonRef" msg="我是父組件傳遞給子組件的msg屬性..."/> <Son1/> </div> </template> <script> import Son from './Son' import Son1 from './Son1' export default { name: "Parent", components: { Son, Son1 }, mounted() { }, methods: { handleEventEmit(params) { console.log('父組件監聽到子組件派發的數據 -->', params); } } } </script> <style scoped> </style>
Son組件
<template> <div> <!-- 子組件綁定派發事件 --> <h3 @click="$emit('eventEmit','我是事件派發想要傳遞的數據123.')">我是son組件</h3> <button @click="sendMsg">給兄弟Son1組件打招呼</button> </div> </template> <script> export default { name: "Son", props: ['msg'], data() { return { sonData: '我是子組件data 的數據...' } }, methods: { sendMsg(){ /* 通過中間組件, 共同父組件來派發事件 */ this.$parent.$emit('sayHiToSiblings','大哥你好,我是你兄弟...') } } } </script> <style scoped> </style>
Son1組件
<template> <div> <h2>我是Son1組件</h2> </div> </template> <script> export default { name: "Son1", mounted() { /* 通過中間組件 共同父組件 監聽事件 */ this.$parent.$on('sayHiToSiblings',(msg)=>{ console.log('%c我是Son1組件, 我接收Son兄弟組件的數據--->','color:pink',msg); }) } } </script> <style scoped> </style>
2.2 $bus
事件總線的方式實現兄弟組件通信
其原理就是通過藉助第三方組件的方式實現通信的.
如果兩個組件之間沒有共同的直接父組件, 我們還可以通過事件總線的方式, 實現跨組件之間的數據通信.
事件總線的方式可以完成任意組件之間的通信.
事件總線通信方式的使用:
1. // 在 main.js 中, 手動在 Vue 的原型上添加一個事件總線 Vue.prototype.$bus = new Vue() // 通過事件總線的 $on 方法,綁定一個方法 // 第一個參數: 事件名字 // 第二個參數: 需要處理的響應函數 this.$bus.$on('toggleTodo',(todo) => { this.toggleTodo(todo) }) 2. // 在其他需要該方法的組件裏通過 $emit() 觸發該事件, 傳入參數 // 第一個參數: 對應事件名字 // 第二個參數: 傳給回調函數的 實參 this.$bus.$emit('toggleTodo',this.todoItem)
案例代碼如下:
main.js
import Vue from "vue"; import App from "./App.vue"; Vue.config.productionTip = false; // 創建一個第三方實例掛到Vue原型, 用來實現事件總線通信 Vue.prototype.$bus = new Vue(); new Vue({ render: h => h(App) }).$mount("#app");
Son組件:
<template> <div> <h3>我是son組件</h3> <p>{{msg}}</p> </div> </template> <script> export default { name: "Son", data() { return { msg: '' } }, mounted() { /* 在組件掛載完的時候完成監聽事件 */ this.$bus.$on('siblingContact', (msg) => { console.log(msg); this.msg = msg }) } } </script> <style scoped> </style>
Son1 組件
<template> <div> <h2>我是Son1組件</h2> <button @click="handleClick">Son1組件點擊, 觸發Son組件傳遞過來的方法 </button> </div> </template> <script> export default { name: "Son1", methods:{ handleClick(){ // 觸發點擊事件的時候, 完成分發事件 this.$bus.$emit('siblingContact','Hi,我是Son1, 我是你的兄弟啊...') } } } </script> <style scoped> </style>
2.3 通過 PubSub.js 實現任意組件間通信
通過第三方的 Js庫, 可以實現任意組件之間的數據通信.
PubSub的使用
消息的 訂閱與發佈 就好比 事件的綁定和 觸發事件
**特點:**PubSub 消息訂閱與發佈這種方式實現通信可以實現任意組件之間的通信.
1. 安裝pubsub-js npm install pubsub-js 2. 在需要 消息訂閱與發佈的文件中, 首先引入 pubsub-js import PubSub from 'pubsub-js' // 注意 pubsub引入的時候, 完整的是 pubsub-js // 訂閱消息, 傳入一個 事件名字, 一個回調函數. 訂閱事件就相當於定義的綁定事件 PubSub.subscribe('MY TOPIC', ()=>{}); // 發佈消息, 傳入一個與訂閱消息相同的 事件名字, 和 數據 // 發佈消息相當於 觸發函數, (給定義的函數傳遞過去參數數據) PubSub.publish('MY TOPIC', 'hello world!');
對 pubsub 消息訂閱機制的理解
#3. 對 pubsub 事件訂閱與發佈的理解 // 事件的訂閱與發佈可以類比傳統的 給元素綁定事件 和 添加響應函數來理解 /** * 1. 在傳統的 添加綁定事件時有兩步 * >1. 給元素添加綁定事件函數, 函數傳入參數. * >2. 給綁定的事件添加響應函數. (注意: 有函數) * * 2. 在消息的訂閱與發佈中 * >1. 發佈消息(傳入參數) ----> 相當於綁定事件 * >2. 訂閱消息('',fn) ----> 相當於添加響應函數 * 因爲訂閱消息中需要兩個參數, 第二個參數爲回調函數, * 回調函數 就相當於我們所說的 事件的響應函數,也就是具體處理邏輯的函數 */
3.0 祖孫組件或嵌套組件之間通信
3.1 provide/inject
實現嵌套組件通信
注意: 通過
provide
和inject
方式實現的嵌套組件之間傳遞的數據, 不是響應式的.
類型:
provide:
Object | ()=> Object
inject:
Array<string> | {[key:string]:string | symbol | Object}
詳細
provide 和 inject 主要在開發高階插件/組件庫時使用。並不推薦用於普通應用程序代碼中。
provide
選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的 property。在該對象中你可以使用 ES2015 Symbols 作爲 key,但是隻在原生支持Symbol
和Reflect.ownKeys
的環境下可工作。
inject
選項應該是:
- 一個字符串數組,或
- 一個對象,對象的 key 是本地的綁定名,value 是:
- 在可用的注入內容中搜索用的 key (字符串或 Symbol),或
- 一個對象,該對象的:
from
property 是在可用的注入內容中搜索用的 key (字符串或 Symbol)default
property 是降級情況下使用的 value
案例代碼如下:
Parent組件:
<template> <div> <h1 style="color: pink">我是parent組件, 下面是Son組件</h1> <button @click="handleClick">點擊修改注入的數據</button> <Son/> </div> </template> <script> import Son from './Son' export default { name: "Parent", data() { return { msg: '我是給後代組件注入的數據' } }, provide() { return { msg: this.msg } }, components: { Son, }, methods: { handleClick() { console.log('修改前 的祖先組件 msg數據 --->> ', this.msg); this.msg = '我修改了祖先組件想要注入給後代組件的數據...' console.log('修改後的祖先組件 msg數據 --->> ', this.msg); } }, mounted() { }, } </script> <style scoped> </style>
Son組件:
<template> <div> <h3>我是son組件</h3> <GrandSon/> </div> </template> <script> import GrandSon from './GrandSon' export default { name: "Son", props: ['msg'], components: {GrandSon}, data() { return { sonData: '我是子組件data 的數據...' } }, methods: {} } </script> <style scoped> </style>
GrandSon組件:
<template> <div> <h2>我是GrandSon組件</h2> <p>{{msg}}</p> </div> </template> <script> export default { name: "Son1", inject:['msg'], mounted() { /* 通過中間組件 共同父組件 監聽事件 */ this.$parent.$on('sayHiToSiblings', (msg) => { // 由此可見,祖先組件點擊按鈕, 修改數據後, 此處數據並沒有修改, 所以數據是非響應式的 console.log('%c我是Son1組件, 我接收Son兄弟組件的數據--->', 'color:pink', msg); }) } } </script> <style scoped> </style>
4.0 插槽的方式實現通信
插槽常用語內容分發. 原理就是佔位符的一個運用
插槽通信的方式後續會詳細介紹.
4.1 普通插槽
4.2 具名插槽
4.3 作用域插槽
總結:
交流學習添加微信(備註技術交流學習):
Gene199302