在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
用于声明组件可以触发的自定义事件(子组件向父组件通信)
使用步骤:
- 用defineEmits定义事件名,得到emits对象
- 用emits函数指定事件名,并传递数据
- 父组件接收时,在子组件标签中触发子组件定义的事件,触发的函数的参数就是传递过来的值
<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>