vue3中的8种组间通信方式
- props
- $emit
- expose/ref
- $attrs
- v-model
- provoid/inject
- vuex
- mitt
props (关键API defineProps)
父传子:<child :data="data"></child>
子接收:
方法一 :setup方法形式(不推荐)
<script>
export default {
props: ["data"],// 如果这行不写,下面就接收不到
setup(props) {
console.log(props.data)
},
}
</script>
方法二:setup语法糖形式(推荐用这种方式)
<script setup>
//defineProps可以不用引入直接使用
const props = defineProps({
// 写法一 data: String
// 写法二 data:{ type:String, default:"" }
})
console.log(props.data)
</script>
两种方法最好别混着用,因为父组件用setup方法形式,子组件用语法糖形式,接收不到父组件里data的属性,只能接收到父组件里setup函数里传的属性
$emit (关键API defineEmits)
父组件传递方法
<template>
<child @myClick="onMyClick"></child>
</template>
<script setup>
import child from "./child.vue"
const onMyClick = (data) => { console.log(data) // 这是父组件收到的信息 }
</script>
子组件操作父组件传递的方法
<template>
// 写法一 <button @click="emit('myClick',"xxx")">按钮</button>
// 写法二 <button @click="handleClick">按钮</button>
</template>
<script setup>
// 方法一 适用于Vue3.2版本 不需要引入
import { defineEmits } from "vue"
// 对应写法一 const emit = defineEmits(["myClick","myClick2"])
// 对应写法二 const handleClick = ()=>{ emit("myClick", "这是发送给父组件的信息") }
// 方法二 不适用于 Vue3.2版本,该版本 useContext()已废弃
//import { useContext } from "vue"
//const { emit } = useContext()
//const handleClick = ()=>{ emit("myClick", "这是发送给父组件的信息") }
</script>
expose / ref (关键API defineExpose,其次是ref.value.xxx调用)
子组件通过defineExpose导出
<script setup>
// 方法一 不适用于Vue3.2版本,该版本 useContext()已废弃
import { useContext } from "vue" const ctx = useContext()
// 对外暴露属性方法等都可以
ctx.expose({ childName: "这是子组件的属性",
someMethod(){ console.log("这是子组件的方法") }
})
// 方法二 适用于Vue3.2版本, 不需要引入
// import { defineExpose } from "vue"
defineExpose({ childName: "这是子组件的属性",
someMethod(){ console.log("这是子组件的方法") }
})
</script>
父组件通过ref调用
<template>
<child ref="comp"></child>
<button @click="handlerClick">按钮</button>
</template> <script setup>
import child from "./child.vue"
import { ref } from "vue"
const comp = ref(null)
const handlerClick = () => {
// 获取子组件对外暴露的属性
console.log(comp.value.childName)
// 调用子组件对外暴露的方法
comp.value.someMethod()
}
</script>
attrs (关键API useAttrs)
attrs:包含父作用域里除 class 和 style 除外的非 props 属性集合
父组件
<child :msg1="msg1" :msg2="msg2" title="3333"></child>
<script setup>
import child from "./child.vue"
import { ref, reactive } from "vue"
const msg1 = ref("1111")
const msg2 = ref("2222")
</script>
子组件接收
<script setup>
import { defineProps, useContext, useAttrs } from "vue"
// 3.2版本不需要引入 defineProps,直接用 const props = defineProps({ msg1: String })
// 方法一 不适用于 Vue3.2版本,该版本 useContext()已废弃 const ctx = useContext()
// 如果没有用 props 接收 msg1 的话就是 { msg1: "1111", msg2:"2222", title: "3333" } console.log(ctx.attrs)
// { msg2:"2222", title: "3333" }
// 方法二 适用于 Vue3.2版本 const attrs = useAttrs() console.log(attrs)
// { msg2:"2222", title: "3333" }
</script>
v-model实现组件通信(注意可以同时绑定多个v-model,关键API defineEmits)
父组件
<child v-model:key="key" v-model:value="value"></child>
<script setup>
import child from "./child.vue"
import { ref, reactive } from "vue"
const key = ref("1111")
const value = ref("2222")
</script>
子组件
<template>
<button @click="handlerClick">按钮</button>
</template>
<script setup>
// 方法一 不适用于 Vue3.2版本,该版本 useContext()已废弃
import { useContext } from "vue" const { emit } = useContext()
// 方法二 适用于 Vue3.2版本,不需要引入
// import { defineEmits } from "vue"
const emit = defineEmits(["key","value"])
// 用法
const handlerClick = () => {
emit("update:key", "新的key")
emit("update:value", "新的value")
}
</script>
provide / inject (通信的形式)
//父组件
<script setup>
import { provide } from "vue"
provide("name", "沐华") //发布信息
</script>
// 子组件
<script setup>
import { inject } from "vue"
const name = inject("name")//订阅信息
console.log(name)
</script>
Vuex/Pinia (useStore)
<script setup>
import { useStore, computed } from "vuex"
const store = useStore()
console.log(store.state.count)
// const count = computed(()=>store.state.count)
// 响应式,会随着vuex数据改变而改变 console.log(count)
</script>
mitt (关键mitt插件)
Vue3 中没有了 EventBus 跨组件通信,但是现在有了一个替代的方案 mitt.js,原理还是 EventBus
先安装 npm i mitt -S
然后像以前封装 bus 一样,封装一下
mitt.js import mitt from 'mitt'
const mitt = mitt()
export default mitt
组间通信
// 组件 A 接收信息
<script setup>
import mitt from './mitt'
const handleClick = () => { mitt.emit('handleChange') }
</script>
// 组件 B 发送信息
<script setup>
import mitt from './mitt' import { onUnmounted } from 'vue'
const someMethed = () => { ... } mitt.on('handleChange',someMethed)
onUnmounted(()=>{ mitt.off('handleChange',someMethed) })
</script>
插槽实现组间通信
默认插槽:
子组件:
<template>
<button @click="handlerClick">
<slot></slot>
</button>
</template>
父组件
<template>
<child>
我是按钮
</child>
</template>
具名插槽:
子组件:
<template>
<button @click="handlerClick">
<slot name="a"></slot>
<slot name="b"></slot>
</button>
</template>
父组件
<template>
<child>
<template v-slot:a>
我是a
</template>
<template #b> //语法糖
我是b
</template>
</child>
</template>
作用域插槽 子组件:
<template>
<button @click="handlerClick">
<slot :$a="a" :$b="b"></slot>
</button>
</template>
<script setup>
import {ref,reactive} from 'vue'
const a = ref('xxx')
const b = reactive({c:'x'})
</script>
父组件
<template>
<child>
<template v-slot="{$a,$b}">
<p> {{$a.a}},{{$b.b.c}} </p>
</template>
</child>
</template>