VUE3(四)、组件通信
1、props
作用:子组件之间的通信。
父传子:属性值的非函数。
子传父:属性值是函数。
父组件:
<template><div>{{ childeData }}</div>——————————————————————————————<child :parentData="parentData" :sendData="getChildData"/>
</template>
<script setup lang="ts" name="parentIndex">
import { ref } from 'vue';
import child from './childeIndex.vue'const childeData=ref("");
const parentData=ref("parentData");function getChildData(data:string){childeData.value=data;
}</script>
子组件:
<template><div>{{ parentData }}</div><button @click="sendData(data)"></button>
</template>
<script setup lang="ts" name="child">
import { ref } from 'vue'
defineProps(['parentData','sendData'])const data=ref('aaa')</script>
2、自定义事件
通过父组件定义函数,子组件调用父组件的函数来实现参数的传递。
父组件:
<template><div>{{ childeData }}</div>——————————————————————————————<child @send-toy="getChildData"/>
</template>
<script setup lang="ts" name="parentIndex">
import { ref } from 'vue';
import child from './childeIndex.vue'const childeData=ref("");function getChildData(data:string){childeData.value=data;
}</script>
子组件:
<template><div>子组件</div><button @click="sendDat"></button>
</template>
<script setup lang="ts" name="child">import { ref } from 'vue'let data=ref('我是子组件')const emit = defineEmits(['send-toy'])function sendDat(){emit('send-toy',data)}</script>
3、mitt
实现任意组件的通信。个人理解:在组件与组件之间提供了一个平台,都向这个平台发送、读取数据。
安装步骤:
- npm i mitt
- 新建文件utiles/emitter.ts
- 在需要的文件中引入emitter,进行绑定 事件,或调用事件。
- 在文件中进行解绑(unmounted中)
emitter.ts
import mitt from 'mitt'
const emitter=mitt()
export default emitter
获取数据的文件
<template><h2>child2中的数据:{{ src }}</h2>
</template>
<script setup lang="ts" name="child1">import { onUnmounted, ref } from 'vue'import emitter from '@/utils/emitter'const src=ref('')//监听事件emitter.on('getData',(data:string)=>{src.value=data})//在组件销毁时取消监听onUnmounted(()=>{emitter.off('getData')})
</script>
发送数据的文件
<template><h2>child2的数据:{{ data }}</h2><button @click="getdata"> 点击获取数据 </button>
</template>
<script setup lang="ts" name="child2">import { ref } from 'vue'import emitter from '@/utils/emitter'const data = ref('222')function getdata(){emitter.emit('getData',data)}
</script>
4、v-model
原理:实现双向绑定,通过传递props参数实现父传子,通过emit传递input或者点击事件实现子传父。
父组件:
<template><!-- 这个写法的本质就是下面那种 --><input type="text" v-model="str"/><input type="text" :value="str" @input="str=(<HTMLInputElement>$event.target).value"/><!-- 自定义组件,一二种方法一致 --><selfInput v-model="str"></selfInput><!-- 此处的$event就是传递的数据 --><selfInput :modelValue="str" @update:modelValue="str=$event"></selfInput></template>
<script setup lang="ts" name="parentIndex">
import { ref } from 'vue';
import selfInput from './selfInput.vue';let str = ref('');
</script>
自定义组件(selfInput.vue):
<template><input type="text" :value="modelValue" @input="emit('update:modelValue',($event.target as HTMLInputElement).value)"/>
</template>
<script setup lang="ts" >
import { ref,defineProps,defineEmits } from 'vue';defineProps(['modelValue'])const emit = defineEmits(['update:modelValue'])</script>
如果自定义自定义组件中的名称,可以实现多个组件的双向绑定。
父组件:
<template>{{ str }} --------{{ pass }}<selfInput v-model:name="str" v-model:pass="pass"></selfInput>
</template>
<script setup lang="ts" name="parentIndex">
import { ref } from 'vue';
import selfInput from './selfInput.vue';let str = ref('');
let pass = ref('');
</script>
子组件:
<template><input type="text" :value="name" @input="emit('update:name',($event.target as HTMLInputElement).value)"/><input type="text" :value="pass" @input="emit('update:pass',($event.target as HTMLInputElement).value)"/>
</template>
<script setup lang="ts" >
import { ref,defineProps,defineEmits } from 'vue';defineProps(['name','pass'])const emit = defineEmits(['update:name','update:pass'])</script>
5、$attrs
作用:适用于当前组件,向子组件通信(祖-->孙)。
当父组件给子组件传递参数,且子组件没有使用props进行接收时,所传递的参数存在attrs中。在子组件中使用$attrs就可以获取所有未被接收的数据。
父组件:
<template><div>{{ a }}</div><hr/><div>{{ b }}</div><hr/><div>{{ c }}</div><hr/><child v-bind="{a,b,c}" @changeA="changeA"></child>
</template>
<script setup lang="ts" name="parentIndex">
import { ref } from 'vue';
import child from './chile.vue';
let a=ref(1);
let b=ref(2);
let c=ref(3);
function changeA(value:number){a.value=value;
}
</script>
子组件:
<template><div>{{ a }}</div><hr/>--------------------------------<sun v-bind="$attrs"></sun>
</template>
<script setup lang="ts" name="chile">import sun from './sun.vue'defineProps(['a'])
</script>
孙组件:
<template><div>{{ b }}</div><hr/><div>{{ c }}</div><hr/><button @click="changeAa">修改a</button>
</template>
<script setup lang="ts" >const num=defineProps(['b','c']);const emit=defineEmits(['changeA']);function changeAa(){emit('changeA',num.b);}
</script>
6、$refs、$parent
$refs:用于父组件修改子组件中的数据。获取所有子组件暴露的数据。
$parent:用于子组件修改父组件的数据。获取父组件所有暴露的数据。
以上的前提:使用defineExpose暴露数据。
父组件:
<template><h1>父组件数据</h1><h1>{{ a }}</h1><button @click="changeChildeA($refs)">修改子组件数据</button><br/>--------------------------------<child1 ref="child1"/>--------------------------------<child2 ref="child2"/>
</template>
<script setup lang="ts" name="parentIndex">
import { ref } from 'vue';
import child1 from './childe1.vue';
import child2 from './childe2.vue';
let a=ref(1);
function changeChildeA(refs:any){refs.child1.b++;refs.child2.b++;
}
defineExpose({a})
</script>
子组件1:
<template> <h1>子组件数据</h1><h1>a:{{ a }}</h1><h1>b:{{ b }}</h1><button @click="changeParent($parent)">修改父组件的a</button>
</template>
<script setup lang="ts" >
import { ref } from 'vue'
let a=ref(1)
let b=ref(1)
function changeParent(parent:any){parent.a++
}
//暴露
defineExpose({b
})
</script>
子组件2:
<template> <h1>子组件数据</h1><h1>a:{{ a }}</h1><h1>b:{{ b }}</h1>
</template>
<script setup lang="ts" >
import { ref } from 'vue'
let a=ref(1)
let b=ref(1)//暴露
defineExpose({a,b
})
</script>
7、provide、inject
作用:实现祖孙组件之间的数据传递。
传递数据:provide(“数据名称”,数据)。
接收数据:inject(“数据名称”,默认值)。
父组件:
<template><h1>父组件数据</h1><h1>{{ a }}</h1>--------------------------------<child1 ref="child1"/>
</template>
<script setup lang="ts" name="parentIndex">
import { ref,provide } from 'vue';
import child1 from './childe1.vue';let a=ref(0);
function changeA(value:number){a.value+=value;
}provide("parent",{a:a,changeA})</script>
子组件:
<template> <h1>子组件数据</h1><h1>a:{{ a }}</h1><div><button @click="changeA(1)">更改父组件的a</button></div>
</template>
<script setup lang="ts" >
import { ref,inject } from 'vue'
let {a,changeA} = inject('parent',{a:ref(0),changeA:function(x:number){}})
</script>
8、插槽
作用:引入组件时可以给组件传递一些HTML元素。
默认插槽
父组件中引入子组件时,子组件标签内部的元素就是会传入slot的参数。
子组件中的slot就相当于占位符,给未来传进来的元素预留空位。
父组件:
<template><h1>父组件数据</h1><div class="child"><child1 :title="'第一个'"><div style="color: aqua;">1111111111</div></child1><child1 :title="'第二个'"><div style="color:black;">22</div></child1></div>
</template>
<script setup lang="ts" name="parentIndex">
import child1 from './childe.vue';
</script>
<style scoped>
子组件:
<div class="child"><h1 style="color: black;">{{ title }}</h1><slot>默认值</slot></div></template>
<script setup lang="ts" >defineProps(['title'])
</script>
具名插槽
作用:可以指定元素插入的位置。
用法:在定义插槽位置时,使用name属性定义其名字。
在应用组件时,在其标签或Templet标签内部定义v-slot(或使用#+名字)属性。
父组件:
<template><h1>父组件数据</h1><div class="child"><child1 :title="'第一个'"><template #b><div style="color: aqua;">1111111111</div></template></child1><child1 :title="'第二个'"><template #a><div style="color:black;">22</div></template></child1></div>
</template>
<script setup lang="ts" name="parentIndex">
import child1 from './childe.vue';
</script>
子组件:
<template> <div class="child"><h1 style="color: black;">{{ title }}</h1><slot name="a">默认值</slot><h1>-------------分隔符--------------------</h1><slot name="b">默认值</slot></div></template>
<script setup lang="ts" >defineProps(['title'])
</script>
作用域插槽
作用:当父组件需要子组件的数据。
用法:在定义slot标签时,使用props写法进行传递参数。
在父组件中使用v-slot拿到参数。
如果同时使用了具名插槽:v-slot:name=‘parms’
父组件:
<template><h1>父组件数据</h1><div class="child"><child1 :title="'第一个'"><template #a="{game,a}"><ul><li v-for="item in game" :key="item.id">{{ item.name }}</li></ul></template></child1></div>
</template>
<script setup lang="ts" name="parentIndex">
import child1 from './childe.vue';
</script>
子组件:
<template> <div class="child"><h1 style="color: black;">{{ title }}</h1><slot name="a" :game="game" :a="a">默认值</slot></div></template>
<script setup lang="ts" >
import { reactive } from 'vue'; defineProps(['title'])const game=reactive([{id:1,name:'a'},{id:2,name:'b'},])const a=1;
</script>