梳理三类vue组件之间的通信

25 阅读2分钟

子组件访问父组件的数据

1. 通过props传递

父组件通过属性传递数据,子组件通过 defineProps 接收

<!-- 父组件 -->
<Child :title="parentTitle" />

<!-- 子组件 -->
<script setup>
const props = defineProps(['title'])
console.log(props.title) // 访问父组件数据
</script>

2. $attrs / useAttrs()

用于透传非 props 的属性和事件(如 classstyle@click 等)。当子组件未声明 props 时,父组件传递的所有属性会自动注入到 $attrs

<!-- 父组件 -->
<Child @custom-event="handleEvent" data-type="external" />

<!-- 子组件 -->
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
// 透传属性到内部元素
<div v-bind="attrs">...</div>
</script>

3. slot标签访问插槽内容

父组件通过插槽向子组件传递 DOM 内容。子组件通过 <slot> 标签控制渲染位置

<!-- 父组件 -->
<Child>
  <template #header> <!-- 具名插槽 -->
    <h1>{{ parentData }}</h1>
  </template>
</Child>

<!-- 子组件 -->
<div>
  <slot name="header" /> <!-- 插槽内容渲染位置 -->
</div>

父组件访问子组件的数据

1. 自定义事件传递子组件数据(defineEmits

子组件通过 defineEmits 声明事件,使用 emit() 触发事件并携带数据

<!-- 子组件 -->
<script setup>
const emit = defineEmits(['update-data'])
emit('update-data', { value: 42 }) // 触发事件,传递子组件数据
</script>

<!-- 父组件 -->
<Child @update-data="(payload) => parentData = payload.value" />

2. ref + defineExpose

父组件通过 ref 获取子组件实例,子组件使用 defineExpose 显式暴露数据/方法

<!-- 父组件 -->
<Child ref="child" />
<script setup>
const childRef = useTemplateRef('child')
childRef.value?.childMethod() // 调用子组件方法
</script>

<!-- 子组件 -->
<script setup>
const childData = ref(0)
defineExpose({ 
  childData,
  childMethod: () => { ... }
})
</script>

3. 作用域插槽 scoped slots

子组件通过 <slot> 暴露数据给插槽内容,父组件在 <template> 中使用 v-slot 接收数据。典型场景: 封装可复用的数据驱动型组件(如表单、列表),父组件控制渲染逻辑。

<!-- 子组件 -->
<slot :item="internalData" /> <!-- 暴露 item 属性 -->

<!-- 父组件 -->
<Child>
  <template v-slot="slotProps">
    {{ slotProps.item }} <!-- 访问子组件数据 -->
  </template>
</Child>

组件的双向通信

1. v-model + defineModel

  • v-model 本质是 :modelValue + @update:modelValue 的语法糖
  • defineModel() 宏自动处理双向绑定
<!-- 父组件 -->
<Child v-model="parentValue" />

<!-- 子组件 -->
<script setup>
const model = defineModel() // 自动绑定
model.value = "new" // 修改触发父组件更新
</script>

2. 依赖注入(provide/inject

父组件用 provide 共享数据,子孙组件用 inject 获取

<!-- 父组件 Provider.vue -->
<script setup>
import { ref, provide, readonly } from 'vue'

// 1. 创建响应式数据
const sharedData = ref('初始数据')

// 2. 创建修改方法(确保单向数据流)
const updateData = (newValue) => {
  sharedData.value = newValue
}

// 3. 提供只读数据和修改方法
provide('sharedData', readonly(sharedData)) // 只读响应式数据
provide('updateSharedData', updateData)     // 修改方法
</script>

总结

props$attrs、插槽slot用于子访问父
emitref + expose、作用域插槽用于父访问子
v-modelprovide/inject 实现双向或跨层通信,灵活组合应对不同场景。