Appearance
组件通信
props用来接受父组件传递过来的数据emit用来子组件传递数据给父组件
props 接受数据
props 数据是只读的,用来接受父组件传递过来的数据。
在子组件中使用
defineProps()来声明 props
ts
<script lang="ts" setup>
// 需要使用defineProps方法去接受父子卷传递过来的数据
const props = defineProps({
money: {
type: Number,
default: 0,
},
message: {
type: String,
default: '',
},
})
console.log(props.message)
</script>emit 修改数据
- props 数据是只读的,不能直接修改。
ts
<script lang="ts" setup>
// 需要使用defineProps方法去接受父子卷传递过来的数据
const props = defineProps({
money: {
type: Number,
default: 0,
},
message: {
type: String,
default: '',
},
})
// 下面代码会报错,应为props是只读的,不能直接修改
props.money = 100;修改方式-computed (推荐)
- 通过计算属性的 get 读取 props 的值,set 时通过 emit 通知父组件更新,既保持响应性,又遵守单向数据流。
ts
<script lang="ts" setup>
// 需要使用defineProps方法去接受父子卷传递过来的数据
const props = defineProps({
money: {
type: Number,
default: 0,
},
message: {
type: String,
default: '',
},
})
// 定义 emit 事件(触发父组件更新)
const emit = defineEmits(['update:money'])
// 计算属性:get 读 props,set 触发 emit
const localMony = computed({
get() {
return props.money; // 从 props 读取值
},
set(newValue) {
emit('update:money', newValue); // 通知父组件更新
},
});
</script>本地复制 props 的值(适合复杂场景)
- 如果需要在子组件内部对值进行复杂处理(如临时修改、验证等),可以在子组件中声明一个本地变量,初始化时复制 props 的值,修改时同步通过 emit 通知父组件。
ts
<script lang="ts" setup>
// 需要使用defineProps方法去接受父子卷传递过来的数据
const props = defineProps({
message: {
type: String,
default: '',
},
money: {
type: Number,
default: 0,
},
})
const localValue = ref(props.message)
// 监听 props 变化,同步到本地变量(父组件主动修改时生效)
watch(
() => props.message,
(newVal) => {
localValue.value = newVal
}
)
// 定义 emit 事件(触发父组件更新)
const emit = defineEmits(['update:message'])
// 本地值变化时,同步到父组件
const syncToParent = () => {
emit('update:message', localValue.value)
}
</script>完整案例
父组件
vue
<template>
<div class="father">
<h2>我是父组件</h2>
<!-- 单一数据,多个绑定 -->
<Child1
v-model:money="父给子的零花钱"
v-model:message="父给子的消息"
></Child1>
<!-- 对象数据,绑定一大推-->
<Child2 v-model:info="info"></Child2>
<!-- 自定义数据,自定义触发事件 -->
<Child3
:info="info"
@handleUpdateInfo="handleUpdateInfo"
></Child3>
</div>
</template>ts
<script lang="ts" setup>
import Child1 from "./Child1.vue"
import Child2 from "./Child2.vue"
import Child3 from "./Child3.vue"
const info = ref({
name: '张三',
age: 18,
sex: '男',
email: 'zhangsan@example.com',
})
// 处理子组件更新事件
const handleUpdateInfo = (newInfo: typeof info.value) => {
info.value = newInfo;
}
</script>子组件1
v-model传递多个单字段数据
vue
<template>
<div class="son">
<h2>我是子组件1</h2>
<!-- 使用方式1:使用props数据 -->
<p>{{ props.info }}</p>
<p>{{ props.money }}</p>
<!-- 使用方式2:可以省略props前缀 -->
<p>{{ info }}</p>
<p>{{ money }}</p>
<button @click="updata()">修改数据</button>
</div>
</template>ts
<script lang="ts" setup>
// 需要使用defineProps方法去接受父子卷传递过来的数据
// defineProps 是vue3提供的方法,不需要引入可以直接使用
let props = defineProps({
money: {
type: Number,
default: 0,
},
message: {
type: String,
default: '',
},
})
// 定义 emit 事件(触发父组件更新)
const emit = defineEmits(['update:money','update:message'])
// 更新数据
const updata = () => {
localMony.value = 100;
localMessage.value = 'hello';
// 更新父组件数据
emit("update:money",localMony.value)
emit("update:message",localMessage.value)
}子组件2
v-model传递对象数据
vue
<template>
<div class="son">
<h2>我是子组件2</h2>
<p>{{ info.name }}</p>
<p>{{ info.age }}</p>
<p>{{ info.sex }}</p>
<p>{{ info.email }}</p>
<button @click="updata()">修改数据</button>
</div>
</template>ts
<script lang="ts" setup>
// 需要使用defineProps方法去接受父子卷传递过来的数据
let props = defineProps({
info: {
type: Object,
default: () => ({
name: '张三',
age: 18,
sex: '男',
email: 'zhangsan@example.com',
}),
}
})
// JavaScript中,对象是引用类型
// 当父组件将一个响应式对象通过props传递给子组件时,
// 子组件接收的不是对象的副本,而是对象的引用地址(即指向同一个 Proxy 代理对象)。
// 这就是说:父组件和子组件访问的是同一个响应式对象(共享同一份内存地址)。
// 所以这里不需要进行额外处理。能够动态拿到父组件传递过来的对象数据。
// 定义 emit 事件(触发父组件更新)
const emit = defineEmits(['update:info'])
// 更新数据
const updata = () => {
// props数据是只读的不能修改
// 所以这需要创建一个新的对象来存储修改后的数据
const newInfo = {
...props.info,
}
newInfo.age = 20;
newInfo.email = 'lisi@example.com';
// 触发父组件更新事件
emit('update:info', newInfo);
}子组件3
- 自定义字段和回调方法传递对象数据
vue
<template>
<div class="son">
<h2>我是子组件2</h2>
<p>{{ info.name }}</p>
<p>{{ info.age }}</p>
<p>{{ info.sex }}</p>
<p>{{ info.email }}</p>
<button @click="updata()">修改数据</button>
</div>
</template>ts
<script lang="ts" setup>
// 需要使用defineProps方法去接受父子卷传递过来的数据
// defineProps 是vue3提供的方法,不需要引入可以直接使用
let props = defineProps({
info: {
type: Object,
default: () => ({
name: '张三',
age: 18,
sex: '男',
email: 'zhangsan@example.com',
}),
}
})
// JavaScript中,对象是引用类型
// 当父组件将一个响应式对象通过props传递给子组件时,
// 子组件接收的不是对象的副本,而是对象的引用地址(即指向同一个 Proxy 代理对象)。
// 这就是说:父组件和子组件访问的是同一个响应式对象(共享同一份内存地址)。
// 所以这里不需要进行额外处理。能够动态拿到父组件传递过来的对象数据。
// 定义 emit 事件(触发父组件更新)
const emit = defineEmits(['handleUpdateInfo'])
// 更新数据
const updata = () => {
// props数据是只读的不能修改
// 所以这需要创建一个新的对象来存储修改后的数据
const newInfo = {
...props.info,
}
newInfo.age = 20;
newInfo.email = 'lisi@example.com';
// 触发父组件更新事件
emit('handleUpdateInfo', newInfo);
}注意事项
v-model在组件上的本质是语法糖:v-model="x"等价于:modelValue="x" @update:modelValue="x = $event"。因此,在子组件中使用
v-model时,需要确保组件有modelValue属性和update:modelValue事件。同理:
v-model:message="x"等价于:message="x" @update:message="x = $event"。在子组件中使用
v-model:message="x"时,需要确保组件有message属性和update:message事件。
