vue3组件通信

330 阅读1分钟

vue3中的8种组间通信方式

  1. props
  2. $emit
  3. expose/ref
  4. $attrs
  5. v-model
  6. provoid/inject
  7. vuex
  8. 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>

转自# Vue3的8种和Vue2的12种组件通信,值得收藏