子組件調用父組件方法
1、直接在子組件中通過 this.$parent.event 來調用父組件的方法
父組件:
<template><p><child></child></p>
</template>
<script>import child from './child';export default {components: {child},methods: {fatherMethod() {console.log('測試');}}};
</script>
子組件:
<template><p><button @click="childMethod()">點擊</button></p>
</template>
<script>export default {methods: {childMethod() {this.$parent.fatherMethod();}}};
</script>
2、父組件使用 v-on 監聽事件,子組件使用 $emit 件觸發事件
@
是 v-on 的縮寫
父組件:
<template><p><child @method1="fatherMethod"></child></p>
</template>
<script>import child from './child';export default {components: {child},methods: {fatherMethod(params) {console.log('測試', params);}}};
</script>
子組件:
<template><p><button @click="childMethod()">點擊</button></p>
</template>
<script>export default {methods: {childMethod() {this.$emit('method1', params); // params 為參數,可不傳 // this.$emit('method1');}}};
</script>
3、父組使用 v-bind 綁定事件,子組件用 props 接收事件
:
是 v-bind 的縮寫
父組件:
<template><p><child :method1="fatherMethod"></child></p>
</template>
<script>import child from './child';export default {components: {child},methods: {fatherMethod() {console.log('測試');}}};
</script>
子組件:
<template><p><button @click="childMethod()">點擊</button></p>
</template>
<script>export default {props: {method1: {type: Function,default: null}},methods: {childMethod() {if (this.method1) {this.method1();}}}};
</script>
父組件調用子組件方法
1、通過 ref 直接調用子組件的方法
父組件:
<template><div><Button @click="fatherMethod">點擊調用子組件方法</Button><Child ref="child"/></div>
</template> <script>
import Child from './child';
export default {methods: {fatherMethod() {this.$refs.child.childMethod();},},
}
</script>
子組件:
<template><div>我是子組件</div>
</template><script>
export default {methods: {childMethod() {console.log('我是子組件的方法');},},
};</script>
2、通過組件的$emit
、$on
方法(可以,但是沒必要)
父組件:
<template><div><Button @click="fatherMethod">點擊調用子組件方法</Button><Child ref="child"/></div>
</template> <script>
import Child from './child';
export default {methods: {fatherMethod() {this.$refs.child.$emit("getChildMethod") //子組件$on中的名字},},
}
</script>
子組件:
<template><div>我是子組件</div>
</template><script>
export default {mounted() {this.$nextTick(function() {this.$on('getChildMethod', this.childMethod);});},methods: {childMethod() {console.log('我是子組件方法');}}
};
</script>
兄弟組件
-
方法1:通過父組件作為中轉
-
通過 ref 和 $parent
-
通過 provide 和 inject
-
-
方法2:使用 EventBus 事件總線
-
方法3:vuex,下一篇內容會講
EventBus 使用方式
1、初始化——全局定義
可以將 eventBus
綁定到 vue
實例的原型上,也可以直接綁定到 window
對象上
//main.js//注冊方式一
Vue.prototype.$EventBus = new Vue();//注冊方式二
window.EventBus = new Vue();
2、監聽事件
//使用方式一
this.$EventBus.$on('eventName', (param1, param2, ...) => {//需要執行的代碼
})//使用方式二
EventBus.$on('eventName', (param1, param2, ...) => {//需要執行的代碼
})
3、觸發事件
//使用方式一
this.$EventBus.$emit('eventName', param1, param2,...)//使用方式二
EventBus.$emit('eventName', param1, param2,...)
4、移除監聽事件
為了避免在監聽時,事件被反復觸發,通常需要在頁面銷毀時移除事件監聽。或者在開發過程中,由于熱更新,事件可能會被多次綁定監聽,這時也需要移除事件監聽。
//使用方式一
this.$EventBus.$off('eventName');//使用方式二
EventBus.$off('eventName');//移除所有
EventBus.$off();
5、示例
簡單示例一:
<!--組件 A.vue-->
<script>
export default { mounted() { // 監聽事件this.$EventBus.$on('custom-event', this.handleEvent) }, methods: { handleEvent(data) { console.log(data) } }
}
</script><!--組件 B.vue-->
<template> <button @click="handleClick">觸發事件</button>
</template> <script>
export default {data() {return {str: '我來自 B 組件'}}methods: {handleClick() {// 觸發事件this.$EventBus.$emit('custom-event', this.str) } }
}
</script>
示例二:
假設兄弟組件有三個,分別是 A、B、C 組件,A 組件如何獲取 B 或者 C 組件的數據
這時候就可以使用 EventBus。EventBus 是一種發布/訂閱模式,用于在組件之間傳遞事件和數據。A 組件可以監聽由 B 或 C 組件發布的事件,并在事件處理函數中獲取傳遞的數據。
思路:
A 組件中使用 Event.$on
監聽事件
B、C 組件中使用 Event.$emit
觸發事件
// A.vue
<template> <div>A 接收到的數據: {{ receivedData }}</div>
</template> <script> export default { data() { return { receivedData: null }; }, mounted() { // 監聽事件 EventBus.$on('custom-event', (data) => { this.receivedData = data.message; }); }, beforeDestroy() { // 組件銷毀前,移除事件監聽器 EventBus.$off('custom-event'); }
};
</script>
// B.vue 和 C.vue
<template> <button @click="sendData">發送數據</button>
</template> <script>
export default { methods: {sendData() { const data = { message: 'I am from B' };// 觸發事件EventBus.$emit('data-from-a', data); } }
};
</script>
多層組件(爺孫)
provide() 和 inject[]
用于將數據或方法暴露給組件樹中的任何后代組件,哪怕是深層次的后代組件都可以訪問到這些數據,而無需通過 props 層層傳遞。
注意:provide 和 inject 主要用于單向數據傳遞,即從祖先組件流向后代組件。雖然可以在后代組件中修改注入的數據,但這種做法會破壞單向數據流的原則,導致數據流向不清晰,難以調試,因此不建議這樣做。
Vue2 用法:
<!--爺/父 組件-->
<template><div id="app"><Children></Children></div>
</template><script>
import Children from "./Children.vue";
export default {name: 'parent',components: { Children },provide() {return {parentEvent: this.myEvent,parentData: this.message,parentStr: '字符串數據'};},data() {return {message: 'data中的數據'}},methods: {myEvent(params1, params2) {console.log(params1, params2)},}
};
</script>
<!--子/孫 組件-->
<template><el-button @click="handleClick">測試</el-button>
</template><script>
export default {name: 'child',inject: ["parentEvent", "parentData", "parentStr"],methods: {handleClick() {this.parentEvent('參數1', '參數2');console.log(this.parentData)console.log(this.parentStr)}}
};
</script>
從上到下依次打印:
參數1 參數2
data中的數據
字符串數據
Vue3 用法:
<!--爺/父 組件-->
<template><div id="app"><Children></Children></div>
</template><script setup>
import { ref, provide } from "vue";
import Children from "./Children.vue";
const message = ref('data中的數據');
const str = '字符串數據'
function myEvent(params1, params2) {console.log(params1, params2)
}
provide('parentEvent', myEvent);
provide('parentData', message);
provide('parentStr', str);
</script>
<!--子/孫 組件-->
<template><el-button @click="handleClick">測試</el-button>
</template><script setup>
import { inject } from "vue";
const parentEvent = inject('parentEvent');
const parentData = inject('parentData');
const parentStr = inject('parentStr', '默認值');function handleClick() {parentEvent('參數1', '參數2')console.log(parentData.value)console.log(parentStr)
}
</script>
細心的朋友已經發現:在 Vue3 中,子組件打印的是 parentData.value,這說明 parentData 是一個響應式對象。
直接總結:
特性 | Vue2 | Vue3 |
---|---|---|
響應式支持 | provide 提供的數據不是響應式的 | provide 提供的數據是響應式的 |
默認值支持 | 不支持默認值 | 支持默認值,inject 的第二個參數就是默認值 |
$attrs 和 $listeners
$attrs
$attrs 是一個對象,包含了父組件傳遞給子組件的所有非 prop 屬性(即沒有在 props 中定義的屬性)。
當你希望將父組件傳遞的屬性傳遞給子組件的子組件時,可以使用 $attrs。
父組件:
<!-- 父組件 -->
<template><div id="app"><Children :params1="params1" :params2="params2" /></div>
</template><script>
import Children from "./Children.vue";
export default {name: 'parent',components: { Children },data() {return {params1: '測試1',params2: '測試2',params3: '測試3',}},mounted() {setTimeout(() => {this.params2 += 'timeout'}, 5000);},
};
</script>
子組件:
<!-- 子組件 -->
<template><div><p>params1: {{ $attrs.params1 }}</p><p>params2: {{ $attrs.params2 }}</p><p>params3: {{ $attrs.params3 }}</p><Groundson v-bind="$attrs" :params4="params4"/></div>
</template><script>
import Groundson from "./Groundson.vue";
export default {name: 'children',components: { Groundson },props: {params1: {type: String,default: ""}},data() {return {params4: '測試4'}},mounted() {console.log("children $attrs:", this.$attrs);},
};
</script>
孫組件:
<!-- 孫組件 -->
<template><div></div>
</template>
<script>export default {name: 'groundson',mounted() {console.log("groundson $attrs:", this.$attrs);},
};
</script>
頁面:
打印:
總結:
-
沒有通過 v-bind 傳遞給子組件的,子組件的 $attrs 中不會有該屬性
-
通過 v-bind 傳遞給了子組件,但是子組件使用了 props 接收的,子組件的 $attrs 中不會有該屬性
-
$attrs 中的屬性值是響應式的
-
在子組件中使用 v-bind=“$attrs” 可以將子組件的 $attrs 中的所有屬性都傳遞給孫子組件,孫子組件也是按同樣的規則接收
inheritAttrs 的作用:
觀察頁面元素發現:
子組件的根元素和孫子組件的根元素都多了一些屬性
官方解釋:默認情況下,父組件傳遞的,但沒有被子組件解析為 props 的 attributes 綁定會被“透傳”。這意味著當我們有一個單根節點的子組件時,這些綁定會被作為一個常規的 HTML attribute 應用在子組件的根節點元素上。我們可以通過設置 inheritAttrs 為 false 來禁用這個默認行為。
例如在子組件中加上 inheritAttrs: false
:
子組件根節點的屬性消失了,由于沒有在孫子組件中設置,孫子組件的根節點還保留著屬性
$listeners
$listeners 包含了父組件傳遞給子組件的所有事件監聽器(即 v-on 綁定的事件)。
與 $attrs 類似, $attrs 是傳遞屬性, $listeners 是傳遞方法。這里就不再舉例了。
注意:在 Vue3 中,$listeners 已經被移除,其功能被合并到了 $attrs 中。