组件通信
一、attrs 通信
描述: attrs
主要用于子组件获取父组件中没有通过 props
或者 emits
接收的属性和自定义事件。
- 父组件
- 子组件
- 孙组件
<template>
<Child :a="a" :b="b" :c="c" @handleChange="handleChange" />
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Child from './components/Child.vue';
const a = ref(0);
const b = ref(1);
const c = ref(2);
const handleChange = value => {
console.log(value);
};
</script>
<template>
<div>
<h3>Child 组件</h3>
{{ $attrs.a }}
{{ $attrs.b }}
{{ $attrs.c }}
<button @click="$attrs.onHandleChange">改变</button>
<GrandChild v-bind="$attrs" />
</div>
</template>
<script setup>
import { useAttrs } from 'vue';
import GrandChild from "./GrandChild.vue"
const attrs = useAttrs();
console.log('attrs', attrs);
</script>
<template>
<div>
<h3>GrandChild 组件</h3>
{{ $attrs.a }}
{{ $attrs.b }}
{{ $attrs.c }}
<button @click="$attrs.onHandleChange">改变</button>
</div>
</template>
<script setup>
import { useAttrs } from 'vue';
const attrs = useAttrs();
console.log('attrs', attrs);
</script>
二、props 通信
描述: 父组件通过 props
传递数据给子组件, 子组件设置 props
属性,定义接收父组件传递过来的数据。
- 父组件
- 子组件
<template>
<Child :count="count" />
</template>
<script setup>
import { ref, watch } from 'vue';
import Child from './components/Child.vue';
const count = ref(0);
watch(count, (newValue, oldValue) => {
console.log(newValue);
});
</script>
<template>
{{ count }}
</template>
<script setup>
defineProps(['count']);
</script>
三、$emit 通信
描述: 子组件通过 $emit
触发自定义事件, 数据以参数的形式传递给父组件
- 父组件
- 子组件
<template>
<Child :count="count" @handleChangeCount="handleChangeCount" />
</template>
<script setup>
import { ref, watch } from 'vue';
import Child from './components/Child.vue';
const count = ref(0);
const handleChangeCount = value => {
count.value = value;
};
watch(count, (newValue, oldValue) => {
console.log(newValue);
});
</script>
<template>
{{ count }}
<button @click="$emit('handleChangeCount', count + 1)">增加</button>
</template>
<script setup>
import { defineEmits } from 'vue';
defineProps(['count']);
defineEmits(['handleChangeCount']);
</script>
四、EventBus
- 父组件
- 子组件
- EventBus.js
<template>
<div id="div-app">
<button @click="handleClick">触发</button>
<Child />
</div>
</template>
<script setup>
import { ref } from 'vue';
import eventBus from "./eventBus.js"
import Child from "./components/Child.vue"
const handleClick = ()=>{
eventBus.$emit('v-click',{ a: 1});
}
</script>
<template>
<div>
</div>
</template>
<script setup>
import { onMounted } from "vue";
import eventBus from "../eventBus.js"
onMounted(()=>{
eventBus.$on('v-click',(value)=>{
console.log(value);
});
});
</script>
class EventBus {
constructor() {
this.events = {};
}
$on(event, fn) {
if (Array.isArray(event)) {
event.forEach(e => {
this.$on(e, fn);
});
} else {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(fn);
}
}
$off(event, fn) {
if (Array.isArray(event)) {
event.forEach(e => {
this.$off(e, fn);
});
}
const cbs = this.events[event];
if (!cbs) {
return;
}
if (!fn) {
this.events[event] = null;
}
let cb;
let i = cbs.length;
while (i--) {
cb = cbs[i];
if (cb === fn) {
cbs.splice(i, 1);
break;
}
}
}
$emit(event, ...args) {
let cbs = this.events[event];
if (cbs) {
cbs.forEach(cb => {
cb.apply(this, args);
});
}
}
$once(event, fn) {
function on() {
this.$off(event, on);
fn.apply(this, arguments);
}
this.$on(event, on);
}
}
export default new EventBus();
五、自定义事件
- 父组件
- 子组件
<template>
<div id="div-app">
<button @click="handleClick">触发自定义事件</button>
<Child />
</div>
</template>
<script setup>
import { ref } from 'vue';
import Child from "./components/Child.vue"
import customEvent from "./customEvent.ts"
const handleClick = ()=>{
const divApp = document.querySelector('#div-app');
customEvent('v-click-div-app',divApp,{ a: 1});
}
</script>
<template>
<div>
</div>
</template>
<script setup>
import { onMounted } from "vue";
onMounted(()=>{
const divApp = document.querySelector('#div-app');
divApp.addEventListener('v-click-div-app',function(e){
console.log("e",e)
});
});
</script>
六、expose & ref 组合通信
描述: 在子组件中,通过 expose
或者 defineExpose
向外暴露属性或者方法, 父组件便可以使用 ref
获取到子组件身上暴露的属性或者方法
- 父组件
- 子组件
<template>
<Child ref="child" />
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Child from './components/Child.vue';
const child = ref(null);
onMounted(() => {
console.log('child', child);
console.log('child.count', child.value.count);
});
</script>
<template>
<div>
{{ count }}
</div>
</template>
<script setup>
import { ref, defineExpose } from 'vue';
const count = ref(1);
defineExpose({
count
});
</script>
七、provide & inject 组合通信
- 父组件
- 子组件
<template>
<div>
{{ a }}
<button @click="changeA(a+1)">父组件按钮</button>
<Child></Child>
</div>
</template>
<script setup lang="ts">
import { provide, reactive, ref } from "vue";
import Child from "./Child.vue"
const a = ref(0);
const changeA = (value: number)=>{
a.value = value;
}
const b = reactive({
a,
changeA,
});
provide('a',a.value);
provide('b',b);
</script>
<template>
<div>
{{ a }}
{{ b.a }}
<button @click="b.changeA(10)">子组件按钮</button>
</div>
</template>
<script setup lang="ts">
import { inject } from 'vue';
const a = inject('a')
const b = inject('b');
</script>
八、v-model & $emit 组合通信
描述: 父组件通过v-model
传递数据,子组件通过$emit('update:字段名',更新后的数据)
更新父组件数据
- 父组件
- 子组件
<template>
<Child v-model:count="count" />
</template>
<script setup>
import { ref, watch } from 'vue';
import Child from './components/Child.vue';
const count = ref(0);
watch(count, (newValue, oldValue) => {
console.log(newValue);
});
</script>
<template>
<input
type="text"
:value="count"
@input="$emit('update:count', $event.target.value)"
/>
</template>
<script setup>
import { defineEmits } from 'vue';
defineProps(['count']);
defineEmits(['update:count']);
</script>