Vue3——父子组件通信

    在Vue开发中,组件通信是核心概念之一。良好的组件通信机制能让我们的应用更加清晰、可维护。

父传子defineProps

    defineProps是一个编译时宏,仅在内部可用,不需要显式导入。声明的 props 会自动暴露给模板。 还返回一个对象,其中包含传递给组件的所有 props,以便我们可以根据需要在 JavaScript 中访问它们:<script setup>defineProps(在一个组件中仅可使用一次)

    在使用前先准备两个父子关系的组件:

<script setup>
import Son from './components/Son.vue';
</script>

<template>
  <div>
    我是父组件App.vue
    <Son></Son>
  </div>
</template>

<style scoped></style>
<script setup>

</script>

<template>
  <div>
    我是子组件Son.app
  </div>
</template>

<style scoped></style>

当前的效果为:

     想要实现父传子的效果,首先要在父组件中定义好数据,在子组件的标签中通过绑定自定义属性的方式,给定一个值;在子组件中使用defineProps函数接收,这其中有很多种情况。

    defineProps()内可使用数组语法或对象语法接收:

数组语法:

  • 使用中括号接收[]
  • 仅声明传递数据的名称,不需要指定类型或其他配置

对象语法:

  • 使用大括号接收{}
  • 需指定传递类型,可指定是否必填,默认值,自定义校验函数等。

注:父组件的中的子标签自定义属性必须与defineProps接收的名称保持一致,或者父组件中使用HTML特性规范,子组件中使用小驼峰命规范 


传递静态数据

数组接收

<script setup>
import Son from './components/Son.vue';
</script>

<template>
  <div>
    我是父组件App.vue
    <Son :props-a="666"></Son>
  </div>
</template>

<style scoped></style>

使用时可直接使用{{propsA}}或{{b.propsA}} 

<script setup>
const b = defineProps(["propsA"])
</script>

<template>
  <div>
    父组件传的值为--{{ b.propsA }}
  </div>
</template>

<style scoped></style>
<script setup>
const b = defineProps(["propsA"])
</script>

<template>
  <div>
    父组件传的值为--{{ propsA }}
  </div>
</template>

<style scoped></style>

<script setup>
import Son from './components/Son.vue';
</script>

<template>
  <div>
    我是父组件App.vue
    <Son :props-a="666" :props-num="'Stringa'"></Son>
  </div>
</template>

<style scoped></style>
<script setup>
const b = defineProps(["propsA","propsNum"])
</script>

<template>
  <div>
    父组件传的值为--{{ propsA }}{{ b.propsNum }}
  </div>
</template>

<style scoped></style>

 对象接收

<script setup>
const b = defineProps({"propsA":Number,"propsNum":String})
</script>

<template>
  <div>
    父组件传的值为--{{ propsA }}{{ b.propsNum }}
  </div>
</template>

<style scoped></style>

传递对象数据

一般情况下传递的值都是响应式的:

<script setup>
import Son from './components/Son.vue';
import { ref } from 'vue';

const str = ref("AppString")
const obj = ref({
  msg:"ObjectMsg",
  massage:"ObjMassage"
})
</script>

<template>
  <div>
    我是父组件App.vue
    <Son :props-a="str" :props-num="obj"></Son>
  </div>
</template>

<style scoped></style>

数组接收

<script setup>
const b = defineProps(["propsA","propsNum"])
</script>

<template>
  <div>
    父组件传的值为--{{ propsA }}{{ b.propsNum }}
  </div>
</template>

<style scoped></style>

这里可以直接使用b.propsNum.msg,不需要.value(在script也是一样),因为父组件在传递整个ref对象时会自动解包,传递ref对象的某个属性时也一样:

<script setup>
const b = defineProps(["propsA","propsNum"])
console.log(b.propsNum);


</script>

<template>
  <div>
    父组件传的值为--{{ propsA }}-{{ b.propsNum }}
  </div>
</template>

<style scoped></style>

对象接收 

使用对象接收时要理清楚接收的类型,传递对象时可直接使用一个大括号

<script setup>
const b = defineProps({
  "propsA":String ,
  "propsNum":{
  }
})


</script>

<template>
  <div>
    父组件传的值为--{{ propsA }}-{{ b.propsNum }}
  </div>
</template>

<style scoped></style>

 不过应该指定其它的类型Object:

<script setup>
const b = defineProps({
  "propsA":String ,
  "propsNum":Object
})


</script>

<template>
  <div>
    父组件传的值为--{{ propsA }}-{{ b.propsNum }}
  </div>
</template>

<style scoped></style>

 

注意:如果父组件直接传递一个响应式数据,那么子组件可以直接修改父组件的值。

<script setup>
import Son from './components/Son.vue';
import { ref } from 'vue';

const str = ref("AppString")
const obj = ref({
  msg:"ObjectMsg",
  massage:"ObjMassage",
  num:88
})
</script>

<template>
  <div>
    我是父组件App.vue{{ obj.msg }}
    <Son :props-a="str" :props-num="obj"></Son>
  </div>
</template>

<style scoped></style>
<script setup>
const b = defineProps({
  "propsA":String ,
  "propsNum":Object
})
const ad =()=>{
  b.propsNum.msg = "ooooo"
}

</script>

<template>
  <div>
    父组件传的值为--{{ propsA }}-{{ b.propsNum.msg }}
    <button @click="ad">修改值</button>
  </div>
</template>

<style scoped></style>

此时点击修改值,会直接修改父组件中的值:

如果传递的是响应式对象的属性,则子组件不能修改其值。

 

 props 展开

    当父组件向子组件传递数据时,如果直接使用 v-bind="object" 而不指定具体属性名,这称为 "props 展开" 或 "对象绑定"。这种方式会将对象的所有属性自动展开为单独的 props。

注:在子组件接收时,属性名和父组件中的属性值要一致

<script setup>
import Son from './components/Son.vue';
import { ref } from 'vue';

const obj = ref({
  msg:"ObjectMsg",
  massage:"ObjMassage",
  num:88
})
</script>

<template>
  <div>
    我是父组件App.vue{{ obj.msg }}
    <Son :="obj"></Son>
  </div>
</template>

<style scoped></style>
<script setup>
const b = defineProps({
  msg:String,
  massage:String,
  num:Number
})

</script>

<template>
  <div>
    父组件传的值为--{{b.msg}}
  </div>
</template>

<style scoped></style>

 在子组件中接收时缺少属性时不会报错,添加属性时,可以使用default给它默认值:

<script setup>
const b = defineProps({
  msg:String,
  massage:String,
  num:Number,
  aa:{
    type:Number,
    required:true,
    default:200
  }
})

</script>

<template>
  <div>
    父组件传的值为--{{b.msg}}{{ b.aa }}
  </div>
</template>

<style scoped></style>

子传父defineEmits

defineEmits 用于声明组件可以触发的自定义事件(子组件向父组件通信)

使用步骤:

  1. 用defineEmits定义事件名,得到emits对象
  2. 用emits函数指定事件名,并传递数据
  3. 父组件接收时,在子组件标签中触发子组件定义的事件,触发的函数的参数就是传递过来的值

<script setup>
import { reactive } from 'vue';
const emit = defineEmits(['e_app'])

const data = reactive({
  msg:""
})

const btn = ()=>{
  emit('e_app',data)
}
</script>

<template>
  <div>
    <input type="text" v-model="data.msg">
    <button @click="btn">提交给父组件</button>
  </div>
</template>

<style scoped></style>
<script setup>
import Son from './components/Son.vue';
import { ref } from 'vue';

const app =(data) =>{
  console.log(data.msg);
  massage.value = data.msg
}

const massage = ref("")
</script>

<template>
  <div>
    我是父组件
    {{ massage }}
    <Son @e_app="app"></Son>
  </div>
</template>

<style scoped></style>

### Vue 3 和 TypeScript 中的组件间通信 #### 使用 `v-on` 实现父子组件间的事件传递 在父组件中可以通过 `v-on` 或者简写的 `@` 来监听来自子组件触发的自定义事件。当子组件内部调用了 `$emit(&#39;eventName&#39;, payload)` 方法时,就会向上传播这个名为 `eventName` 的事件给父级组件,并携带参数 `payload`。 ```html <!-- 父组件 Parent.vue --> <template> <div> <!-- 子组件触发 customEvent 后会执行 handleCustomEvent 函数 --> <Child @customEvent="handleCustomEvent"/> </div> </template> <script lang="ts"> import { defineComponent, ref } from &#39;vue&#39;; export default defineComponent({ setup() { const handleCustomEvent = (message: string) => { console.log(`接收到的消息是 ${message}`); }; return { handleCustomEvent, }; }, }); </script> ``` 子组件则负责发射该事件: ```html <!-- 子组件 Child.vue --> <template> <button @click="sendEvent">发送消息给父组件</button> </template> <script lang="ts"> import { defineComponent } from &#39;vue&#39;; export default defineComponent({ methods: { sendEvent(): void { this.$emit(&#39;customEvent&#39;, &#39;你好&#39;); } } }); </script> ``` 这种方式适用于具有明确层次结构的父子组件之间通讯[^1]。 #### 利用 `$attrs` 进行属性透传 对于某些场景下希望将未声明过的 prop 自动向下一层嵌套的子组件传播,则可以利用 `$attrs` 对象配合 `inheritAttrs: false` 属性完成此操作。这允许开发者更灵活地处理多层嵌套情况下的数据流动问题。 ```html <!-- 父组件 Father.vue --> <template> <div class="father"> <h3>父组件</h3> <Grandson v-bind="$attrs" /> </div> </template> <script setup lang="ts"> // ... </script> ``` 这里假设有一个孙子组件 Grandson 接收到了这些额外绑定过来的数据[^3]。 #### 应用提供/注入 API (`provide/inject`) 除了上述两种较为常见的模式外,Vue 提供了一种更为高级的方式来进行跨层级依赖注入——即 provide 和 inject 配合使用。这种方法特别适合于那些需要在整个应用范围内共享状态或服务的情况。 ```typescript // App.vue or some parent component setup() { const sharedState = reactive({ count: 0 }); provide(&#39;sharedState&#39;, readonly(sharedState)); } ``` 而在接收方(可能是任何后代组件),只需要简单地通过 `inject()` 获取即可访问到被提供的资源: ```typescript const state = inject(&#39;sharedState&#39;) as any; console.log(state.count); ``` 这种机制非常适合用来替代全局事件总线的功能,在不破坏原有逻辑的前提下实现了更加优雅的状态管理方案[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值