五vue3.0之组件通信详解(definePropsdefineEmitsdefineExpose)
Posted arguments_zd
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了五vue3.0之组件通信详解(definePropsdefineEmitsdefineExpose)相关的知识,希望对你有一定的参考价值。
我们在做vue项目中,我们总会遇到组件引入,在嵌套组件中我们的父级组件中引入子级组件中的值,或者在子组件中我们使用父组件中的值。那么当我们遇到这样的场景我们应该怎么做,在vue2.0中,我们使用props和emit进行父子之间的通信,兄弟之间用事件中央总线(event bus);在vue3.2的语法中我们则使用defineProps和defineEmits来声明props和emit,用其进行组件之间的传值,那么接下来,我们来看看。
defineProps:
1、用于组件通信中父级组件给子级组件传值,其用来声明props,其接收值为props选项相同的值
2、默认支持常见的类型检查,在ts下,我们需要明确变量的类型,类型经常是我们的自定义类型
3、只能在<script setup>中使用
4、不需要被导入即可使用,它会在编译<script setup>语法块时一同编译掉
5、必须在<script setup>的顶层使用,不可以在<script setup>的局部变量中引用
6、不可以访问 <script setup> 中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中
/ 父级组件使用自定义属性向下传递值
<div class="home">
<HelloWorld :msg="msg"/>
</div>
<script setup>
import HelloWorld from \'@/components/HelloWorld\'
/**
* 父级组件
vue 组件通信
1. 子组件间通信(defineEmits + defineProps)
1.1 实现效果
在一个子组件的输入框中输入数据, 在另一个子组件上显示. 如下图:
1.2 defineEmits 和 defineProps 的 TS 使用
1.2.1 defineEmits 的 TS 使用
export declare function defineEmits<TypeEmit>(): TypeEmit;
// 根据上面的定义, 我们可以知道通过传入函数签名可以为其返回的 emit 方法定义类型, 对 emit 进行精确的类型定义
const emit = defineEmits<
(e: \'update-age\', data: number): void
(e: \'update-name\', data: string): void
>()
1.2.2 defineProps 的 TS 使用
export declare function defineProps<TypeProps>(): Readonly<Omit<TypeProps, BooleanKey<TypeProps>> &
[K in keyof Pick<TypeProps, BooleanKey<TypeProps>>]-?: NotUndefined<TypeProps[K]>;
>;
/**
* defineProps<
* propName1: TSType,
* propName2: TSType,
* ......
* >()
*/
1.2.2.1 另外两种 JS 使用
export declare function defineProps<PropNames extends string = string>(props: PropNames[]): Readonly<
[key in PropNames]?: any;
>;
// defineProps([\'propName1\', \'propName2\', ...])
export declare function defineProps<PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions>(props: PP): Readonly<ExtractPropTypes<PP>>;
export declare type ComponentObjectPropsOptions<P = Data> =
[K in keyof P]: Prop<P[K]> | null;
;
export declare type Prop<T, D = T> = PropOptions<T, D> | PropType<T>;
declare interface PropOptions<T = any, D = T>
type?: PropType<T> | true | null;
required?: boolean;
default?: D | DefaultFactory<D> | null | undefined | object;
validator?(value: unknown): boolean;
declare type Data = Record<string, unknown>;
/**
* defineProps(
* propName1:
* type: propName1Type,
* required: isRequired,
* default: defaultValue
* ,
* propName2: propName2Type,
* ......
* )
*/
1.3 具体实现
1.3.1 children-component-one
<!-- children-component-one -->
<script setup lang="ts">
import ref from \'vue\'
const inputValue = ref(\'\')
const emit = defineEmits<
(e: \'update-input-text\', data: string): void
>()
const handleInput = () =>
emit(\'update-input-text\', inputValue.value)
</script>
<template>
<div class="children-component-one">
inputValue: <input type="text" v-model="inputValue" @input="handleInput" />
</div>
</template>
1.3.2 children-component-two
<!-- children-component-two -->
<script setup lang="ts">
defineProps<
inputTextValue: string
>()
</script>
<template>
<div class="children-component-two">
show: inputTextValue
</div>
</template>
1.3.3 parent-component
<!-- app.vue -->
<script setup lang="ts">
import ref from \'vue\'
import childrenComponentOne from \'./views/children-component-one.vue\'
import childrenComponentTwo from \'./views/children-component-two.vue\'
const inputText = ref(\'\')
const inputTextUpdateHandle = (newValue: string) =>
inputText.value = newValue
</script>
<template>
<div class="app">
<h2>app</h2>
<children-component-one @update-input-text="inputTextUpdateHandle"/>
<children-component-two :input-text-value="inputText" /> <!-- 父组件向子组件传值 -->
</div>
</template>
通过 defineEmits
可以实现子组件向父组件传值
通过 defineProps
可以实现子组件接收父组件传来的值
两者配合即可实现子组件间的通信
2. 父子组件间的通信(v-model + defineEmits + defineProps)
2.1 实现效果
父组件有一计数器 counter
, 子组件有一 button
, 通过点击 button
可以使得 counter + 1
, 而 counter
的显示则交由父组件
2.2 具体实现
2.2.1 children-component
<script setup lang="ts">
const props = defineProps<
counter: number
>()
const emit = defineEmits<
(e: \'update:counter\', data: number): void
>()
const handleClick = () =>
emit(\'update:counter\', props.counter + 1)
</script>
<template>
<div class="children-component">
<h2>children-component</h2>
<button @click="handleClick">add one</button>
</div>
</template>
2.2.2 parent-component
<script setup lang="ts">
import ref from \'vue\'
import childrenComponent from \'../children-component/children-component.vue\'
const counter = ref(0)
</script>
<template>
<div class="parent-component">
<h2>parent-component</h2>
Counter: counter
<children-component v-model:counter="counter" />
</div>
</template>
3. 父子组件间的通信(ref + defineExpose)
3.1 实现效果
子组件中有一计数器 counter
和一个能令 counter + 1
的 button
, 且子组件将传递 counter
给父组件, 由父组件来进行展示. 如下图所示:
3.2 defineExpose 的 TS 使用
declare function defineExpose<Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed): void;
/**
* const passValue: PassType = someValue
* defineExpose(passValue)
*/
3.3 具体实现
3.3.1 parent-component
<script setup lang="ts">
import ref, type Ref from \'vue\'
import childrenComponent from \'../children-component/children-component.vue\'
import type PassType from \'@/types\'
const passValue = ref() as Ref<PassType>
</script>
<template>
<div class="parent-component">
<h2>parent-component</h2>
Counter: passValue.counter
<children-component ref="passValue" />
</div>
</template>
3.3.2 children-component
<script setup lang="ts">
import ref from \'vue\'
import type PassType from \'@/types\';
const counter = ref(0)
const handleClick = () =>
counter.value++
const passValue: PassType = counter
defineExpose(passValue)
</script>
<template>
<div class="children-component">
<h2>children-component</h2>
<button @click="handleClick">add one</button>
</div>
</template>
3.3.3 src/types/index.ts
export interface PassType
counter: Ref<number>
4. 祖先-后代组件间的通信(Provide + Inject)
由祖先节点通过 Provide 提供需要传递的值, 而该节点的任何后代节点都可以通过 Inject 接收传递的值
4.1 实现效果
祖先组件拥有一个 counter
和一个可以令 counter + 1
的 button
, 并将 counter
传递给后代组件, 交由后代组件来进行展示
4.2 Provide + Inject 的 TS 使用
declare function provide<T>(key: InjectionKey<T> | string | number, value: T): void
declare function inject<T>(key: InjectionKey<T> | string): T | undefined
export declare interface InjectionKey<T> extends Symbol
// 由这行代码可知: InjectionKey<T> 类型可以接收 Symbol 类型的值
// 由上面的代码块可知
// 用法 1 :
const value: ValueType = someValue
const key: InjectionKey<ValueType> = Symbol(\'key-name\')
provide(key, value)
const passValue: ValueType = inject(key)
// 用法 2:
const value: ValueType = someValue
provide(\'pass-value\', value)
const passValue = inject<ValueType>(\'pass-value\')
4.3 具体实现
4.3.1 src/types/index.ts
import type InjectionKey, Ref from \'vue\'
export type CounterType = Ref<number>
export const key: InjectionKey<CounterType> = Symbol(\'CounterType\')
4.3.2 祖先组件
<script setup lang="ts">
import key from \'@/types\';
import provide, ref from \'vue\'
import childrenComponent from \'../children-component/children-component.vue\'
const counter = ref(0)
const handleClick = () =>
counter.value++
provide(key, counter)
</script>
<template>
<div class="parent-component">
<h2>parent-component</h2>
<button @click="handleClick">add one</button>
<children-component />
</div>
</template>
4.3.3 后代组件
<script setup lang="ts">
import inject from \'vue\'
import key from \'@/types/index\'
const counter = inject(key)
</script>
<template>
<div class="children-component">
<h2>children-component</h2>
Counter: counter
</div>
</template>
5. 状态管理工具(Pinia / Vuex)
以上是关于五vue3.0之组件通信详解(definePropsdefineEmitsdefineExpose)的主要内容,如果未能解决你的问题,请参考以下文章
「 VUE3 + TS + Vite 」父子组件间如何通信?