Vue3 多种组件通讯方法

345 阅读1分钟

父组件传值给子组件(简称:父传子) defineProps

// 父组件 
<template>
  <!-- 使用子组件 -->
  <Child :msg="message" />
</template>
<script setup>
    import Child from './Child.vue' // 引入子组件
    let message = '你好 vue3'
</script>

// 子组件
<template>
  <div>
    {{ msg }}
  </div>
</template>
<script setup>
const props = defineProps({
  msg: {
    type: String,
    default: ''
  }
})
console.log(props.msg) // 打印需要使用 props.msg,在html中使用只需要 {{props}}
</script>

defineProps 其实还能做很多事情,比如:设置默认值 default ,类型验证 type ,要求必传 required ,自定义验证函数 validator 等等。 它在 script setup 中是直接可用不需要额外引入。更多细节请看文档。
官网地址: props 文档

子组件通知父组件触发一个事件并且传值给父组件。(简称:子传父)defineEmits

// 父组件
<template>
  <div>父组件:{{ message }}</div>
  <!-- 自定义 changeMsg 事件 -->
  <Child @changeMsg="changeMessage" />
</template>

<script setup>
    import { ref } from 'vue'
    import Child from './Child.vue'
    let message = ref('Hello vue3')
    // 更改 message 的值,data是从子组件传过来的
    const changeMessage = (data) => {
      message.value = data
    }
</script>

// 子组件
<template>
  <div>
    子组件:<button @click="handleClick">子组件的按钮</button>
  </div>
</template>
<script setup>
    // 注册一个自定义事件名,向上传递时告诉父组件要触发的事件。
    const emit = defineEmits(['changeMsg'])
    const handleClick = ()=> {
      // 参数1:事件名
      // 参数2:传给父组件的值
      emit('changeMsg', '你好 vue3')
    }
</script>

和 defineProps 一样,在 script setup 中必须使用 defineEmits API 来声明 emits,它在 script setup 中是直接可用不需要额外引入。更多细节请看文档。
官网地址: emits文档

expose 导出 / ref 获取

// 父组件 
<template>
  <div>父组件:拿到子组件的msg数据:{{ msg }}</div>
  <button @click="callChildFn">调用子组件的方法</button>
  <Child ref="com" />
</template>

<script setup>
    import { ref, onMounted } from 'vue'
    import Child from './Child.vue'
    const com = ref(null) // 通过 模板ref 绑定子组件
    const msg = ref('')
    onMounted(() => {
      // 在加载完成后,将子组件的 message 赋值给 msg
      msg.value = com.value.message
    })
    const callChildFn = ()=> {
      // 调用子组件的 changeMessage 方法
      com.value.changeMessage('你好 vue3')
      // 重新将 子组件的message 赋值给 msg
      msg.value = com.value.message
    }
</script>

// 子组件
<template>
  <div>子组件:{{ message }}</div>
</template>
<script setup>
    import { ref } from 'vue'
    const message = ref('Hello vue3')
    const changeMessage = (data)=> {
      message.value = data
    }
    使用 defineExpose 向外暴露指定的数据和方法
    defineExpose({
      message,
      changeMessage
    })
</script>

provide / inject 多层级传递

provide 是在父组件里使用的,可以往下传值。
inject 是在子(后代)组件里使用的,可以网上取值。
provide / inject 文档 image.png

// 父组件
<template>
  <Child></Child>
</template>
<script setup>
    import { ref, provide, readonly } from 'vue'
    import Child from './Child.vue'
    const name = ref('往下')
    const msg = ref('你好 vue3')
    // 使用readonly可以让子组件无法直接修改,需要调用provide往下传的方法来修改
    provide('name', readonly(name))
    provide('msg', msg)
    provide('changeName', (value) => {
      name.value = value
    })
</script>

//  在不知道要多少层的子组件下调用参数
<template>
  <div>
    <div>msg: {{ msg }}</div>
    <div>name: {{name}}</div>
    <button @click="handleClick">修改</button>
  </div>
</template>
<script setup>
    import { inject } from 'vue'
    const name = inject('name', 'hello') // 看看有没有值,没值的话就适用默认值(这里默认值是hello)
    const msg = inject('msg')
    const changeName = inject('changeName')
    const handleClick ()=> {
        // 子组件
      // name.value = 'vue3'
      // 这样写不行,因为vue里推荐使用单向数据流,当父级使用readonly后,这行代码是不会生效的。
      // 正确的方式
      changeName('再往下')
      // 因为 msg 没被 readonly 过,所以可以直接修改值
      msg.value = 'Hello'
    }
</script>