最近写一个组件需要父子插槽之间的通信,有一次暴露了自己比较菜的事实。
csdn上还是有很多文章介绍vue的插槽的,一共就三个嘛,普通、具名和作用域插槽,结合一下还有个具名作用域插槽。讲插槽之间通信的文章的话不多,基本上也是靠各位发挥。
我第一个想法是利用父子通信方式,抛出自定义事件,但实际上父组件当中的slot位置,是不好挂载自定义事件的,比如如下的写法是不行的:
//father
<template>
this is father
<slot @test="fn"></slot> //slot上面加事件是不行的
<template>
第二个想到的想法好像是vue2当中有利用this.$on的方式来配合emit监听事件,有了这个this.$on,我们就能在生命周期或者其他地方进行监听子组件(slot)当中的内容emit当中的事件,比如如下的方式(假设slot当中的子组件抛出了一个test事件)
// father
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
mounted(){
this.$on('test', this.fn)//利用这种方式进行监听
},
methods: {
fn(val){
console.log(val)
}
},
}
</script>
有点iframe通信的感觉,但是这个写法在vue3当中被削掉了
第三种想法就是使用作用域插槽,既然作用域插槽能传数据,为什么不往里面传入一个能改变父组件状态的函数呢,如下:
father:
<template>
{{ `i am father,this is my name: ${name}` }}
<br />
<slot name="slot1" :user="name" :changeName="changeName"></slot>
</template>
<script setup lang="ts">
import { ref } from "vue"
let name = ref<string>("huangbapi")
let changeName = () => {
name.value = "gss"
}
</script>
这边使用了具名作用域插槽,传入user和changeName两个参数,注意name参数是插槽独有的。
son:
<template>
i am son
<br />
<button @click="() => { props.changeName() }">change father's name</button>
</template>
<script setup lang="ts">
interface Props {
user: string
changeName: () => void
}
let props = defineProps<Props>()
</script>
对插槽进行组合
<template>
<h1>page1</h1>
<Father>
<template #slot1="{ user, changeName }">
<Son :user="user" :change-name="changeName"></Son>
</template>
</Father>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Father from "../components/Father.vue";
import Son from "../components/Son.vue"
</script>
ok,这种写法结合组件的生命周期,估计能写不少东西