Vue3.2 多种组件通信方式

298 阅读1分钟

查看原文

props

父组件传值给子组件

文档:props

emits

子组件调用父组件中的方法,并且可以传值给父组件

文档:emits

父组件:

<ChildComponent @loadData='fatherFun'/>
const fatherFun = (params:any) => {
  console.log(params)
};

子组件:

//不需要引入 defineEmits
const emit = defineEmits(['loadData']) 
const sonFun = () => {
  emit('loadData', '我是来自子组件的参数哦~')
};

expose / ref

子组件通过 expose 暴露自身的方法和数据

父组件通过 ref 获取子组件,调用其方法或访问数据

文档:exposedefineExpose

子组件:

//不需要引入 defineExpose
const childName = ref("这是子组件的属性");
defineExpose({
  childName,
  childMethod(){ 
    console.log("这是子组件的方法") 
  } 
});

父组件:

// 注意 ref="childRef"
<template>
    <ChildComponent ref="childRef"></ChildComponent>
    <button @click="handlerClick">按钮调用子组件方法</button>
</template>
<script setup lang="ts">
    import ChildComponent from "./ChildComponent.vue"
    import { ref } from "vue"
    const childRef = ref<InstanceType<typeof ChildComponent>>()
    const handlerClick=()=>{
        // 获取子组件 childName 属性
        console.log(childRef.value?.childName)
        // 调用子组件 childMethod 方法
        childRef.value?.childMethod() 
    }
</script>

attrs

在子组件中,没使用 prop 或 emits 定义的 attribute,可以通过 $attrs 来访问

文档:透传 Attributes

父组件:

<child msg1="msg1" msg2="msg2" msg3="33" />

子组件:

<script setup lang="ts">
    import { useAttrs } from "vue"
    // 不需要引入 defineProps
    const props = defineProps({
        msg1: String
    })
    const attrs = useAttrs()
    console.log(attrs) 
    // 结果:{ msg2:"22", msg3: "33" }
    // 注释 defineProps 结果:{ msg1: "11", msg2:"22", msg3: "33" }
</script>

v-model

组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件,可以支持多个数据双向绑定

v-model组件事件配合 v-model 使用

单值 / 修饰符

  • 组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件

  • v-model 可通过 . 的方式传入自定义的修饰符

    内置的修饰符,例如 .trim.number 和 .lazy

父组件:

<template>
   <Child v-model="messageFromParent"/>
   // <Child v-model.uppercase="messageFromParent"/>
</template>
const messageFromParent = ref('aaaa')

子组件:

<template>
    <div>messageFromParent:{{modelValue}}</div>
    <button @click="handleClick">点击修改</button>
</template>
<script setup lang="ts">
    // v-model没有指定参数
    
    // 接收父组件使用 v-model 传进来的值以及修饰符
    const props = defineProps([
      'modelValue',
      'modelModifiers'
    ])
    // 通知父组件修改值:update:modelValue
    const emit = defineEmits(['update:modelValue']) 
    function handleClick() {
      emit('update:modelValue', 'bbb')
      if (props.modelModifiers?.uppercase) {
        emit('update:modelValue', props.modelValue.toUpperCase())
      }
    }
</script>

如果没有其他逻辑,直接触发修改,可以优化成:

<template>
    <div>messageFromParent:{{modelValue}}</div>
    <button @click="$emit('update:modelValue', 'bbb')">点击修改父组件的 messageFromParent</button>
</template>
<script setup lang="ts">
    defineProps([
      'modelValue' 
    ])
</script>

如果指定了参数,不用默认的modelValue,相应修改:

<ChildComponent v-model:msg="messageFromParent" />

//子组件需要声明一个 `msg` prop
defineProps(['msg'])
//通过 `update:msg` 事件更新父组件值
defineEmits(['update:msg'])

多个 v-model 绑定

父组件:

<child v-model:msg1="msg1" v-model:msg2="value"></child>
<script setup>
    import child from "./child.vue"
    import { ref } from "vue"
    const msg1 = ref("1111")
    const msg2 = ref("2222")
</script>

子组件:

<template>
    <div>msg1:{{msg1}}</div>
    <div>msg2:{{msg2}}</div>
    <button @click="handlerClick">修改父组件msg1、msg2的值</button>
</template>
<script setup>
    // 接收
    defineProps({
      msg1: String,
      msg2: String
    })
    // 不需要引入 defineEmits
    const emit = defineEmits(['update:msg1', 'update:msg2'])
    const handlerClick = () => {
        emit("update:msg1", "新的msg1")
        emit("update:msg2", "新的msg2")
    }
</script>

插槽 slots

插槽可以理解为父组件传一段 HTML 片段给子组件。子组件将 <slot> 元素作为承载分发内容的出口

插槽 Slots

provide / inject

跨层级组件通信,不限层级

provide-inject

顶级组件A:

<script setup lang='ts'>
    import { provide } from "vue";
    provide("username", "aaa");
</script>

顶级组件B:

<script setup lang='ts'>
    import { provide } from "vue";
    provide("username", "bbb");
</script>

底层组件:

<script setup lang='ts'>
    import { inject } from "vue";
    const username = inject("username");
    console.log(username);
</script>

Vuex / Pinia

vuex 文档

Pinia 文档

mitt

父子、兄弟、跨层级组件通信

详见官方示例

安装:

$ npm install --save mitt

封装mitt.ts

import mitt from 'mitt' 
const Mitt =mitt()
export default Mitt

父组件:

<template>
  <Child1 />
  <Child2 />
</template>

子组件 Child1:

<template>
  子组件1
  <button @click="handleClick">打声招呼</button>
</template>
<script setup lang="ts">
import Mitt from '@/utils/mitt';
function handleClick() {
  Mitt.emit('sayHello')
}
</script>

子组件 Child2:

<template>
  子组件2
</template>
<script setup lang="ts">
import Mitt from '@/utils/mitt'
Mitt.on('sayHello', () => console.log('兄弟你好'))
</script>