跳到主要内容

组件通信

defineProps/defineEmits

文档

父组件:

<template>
<Child :name="data" @update="update" />
</template>

<script setup>
import Child from './Child.vue';
const data = ref('hello');
function update(e) {
console.log(e);
}
</script>

子组件:

1、拿来直接在模板里使用:

<script setup>
defineProps(['name']);

// const props = defineProps(['msg'])
</script>

<template>
<h1>{{name}}</h1>
<!-- <h2>{{props.msg}}</h2> -->
</template>

2、如果要对接收的数据做限制,如数据类型、默认值等,方式如下:

const props = defineProps({
name: {
type: String,
default: ''
}
});

console.log(props.name);

3、在定义 props 时,除了上面类似 vue2 中的方式,还可以使用 TS,用 withDefaults 函数给 props 添加默认值。

import { defineProps, withDefaults } from 'vue';

interface Props {
name: string;
list?: string[];
}

const props = withDefaults(defineProps<Props>(), {
name: 'zs',
list: () => [1, 2, 3]
});
注意

可变引用类型的默认值应封装在函数中,如数组或对象

4、emit 事件声明:

const emit = defineEmits(['delete', 'update']);

// 类型声明
const emit = defineEmits<{
delete: [id: number];
update: [name: string, ...rest: any[]];
}>();

// 触发父组件的方法
emit('update', { msg: '传入数据成功' });

这里的 delete、update 是事件名称,后面是事件参数。

v-model/defineModel

文档

双向绑定

Parent.vue
<template>
<Foo v-model:name="name" />
</template>

<script setup>
const name = ref(666);
</script>
Child.vue
<script setup>
import { defineModel } from 'vue';

const namedata = defineModel('name');
</script>

<template>
<input type="text" v-model="namedata" />
</template>

依赖注入

provide/inject依赖注入,避免逐层传递数据

父组件:

Parent.vue
<template>
<Son />
</template>

<script setup>
import Son from './Son.vue';
const count = ref(0);

function updateCount(value) {
count.value += value;
}

provide('countContext', { count, updateCount });
</script>

孙子组件:

GrandChild.vue
<script setup>
const { count, updateCount } = inject('countContext', { count: 0, updateCount: (num: number) => {} });
</script>

<template>
<h1>{{ count }}</h1>
<button @click="updateCount(2)">click</button>
</template>

inject 的第二个参数是可选的默认值

$attrs

$attrs 可以用于祖孙组件传值

父组件:

Parent.vue
<template>
<Child :name="name" :age="age" @update="update" />
</template>

<script setup>
import Child from './Child.vue';

const name = ref('hello');
const age = ref(18);

function update(value) {
age.value += value;
}
</script>

儿子组件:

Child.vue
<script setup>
import GrandChild from './GrandChild.vue';
</script>

<template>
<GrandChild v-bind="$attrs" />
</template>

孙子组件:

GrandChild.vue
<script setup>
defineProps(['name', 'age']);
const emit = defineEmits(['update']);
function fn() {
emit('update', 2);
}
</script>

<template>
<h1>{{ name }}</h1>
<h1>{{ age }}</h1>
<button @click="fn">click</button>
</template>

在给子组件传递方法时,也可以将方法当作属性传递,如 <Child :age="age" :update="update" />,子组件通过defineProps接收,如下:

<script setup>
defineProps(['age', 'update']);
</script>

<template>
<h1>{{ age }}</h1>
<button @click="update(2)">click</button>
</template>

$refs/$parent

  • $refs 用于 父传子
  • $parent 用于 子传父
<script setup>
import { ref } from 'vue';
import Child1 from './Child1.vue';
import Child2 from './Child2.vue';

let c1 = ref();
let c2 = ref();
const age = ref(18);

function updateChild1() {
console.log(c1.value);
c1.value.a = '在父组件操作子组件Child1的数据';
}

function updateChild2() {
c2.value.c = '在父组件操作子组件Child2的数据';
}

function getAllChild(refs) {
console.log(refs);
}

// 把数据交给外部
defineExpose({ age });
</script>

<template>
<h1>{{ age }}</h1>
<button @click="updateChild1">修改Child1</button>
<button @click="updateChild2">修改Child2</button>
<button @click="getAllChild($refs)">获取所有的子组件实例对象</button>
<Child1 ref="c1" />
<Child2 ref="c2" />
</template>
Child1.vue
<script setup>
import { ref } from 'vue';
const a = ref('aa');
const b = ref('bb');

function updateParent(parent) {
console.log(parent);
parent.age += 1;
}

// 把数据交给外部
defineExpose({ a, b });
</script>

<template>
<h1>{{ a }}</h1>
<h1>{{ b }}</h1>
<button @click="updateParent($parent)">操作父组件的数据</button>
</template>
Child2.vue
<script setup>
import { ref } from 'vue';
const c = ref('cc');
const d = ref('dd');

defineExpose({ c, d });
</script>

<template>
<h1>{{ c }}</h1>
<h1>{{ d }}</h1>
</template>

插槽

插槽可以实现父子组件通信,详见插槽