组件通信
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>
插槽
插槽可以实现父子组件通信,详见插槽