vue3常用的组件间通信

&白帝& 2024-10-03 15:33:02 阅读 93

一 props

props 可以实现父子组件通信,props数据是只读。

1. 基本用法

在父组件中,你可以这样传递 props:

<code><template>

<ChildComponent message="Hello, Vue 3!" />code>

</template>

<script setup>

import ChildComponent from './ChildComponent.vue';

</script>

在 Vue 3 的 <script setup> 语法中,你可以使用 defineProps 来定义 props:

<script setup>

import { -- --> defineProps } from 'vue';

const props = defineProps({

title: String,

count: Number

});

</script>

在传统的 export default 语法中,可以这样定义:

export default {

props: {

title: {

type: String,

required: true

},

count: {

type: Number,

default: 0

}

}

};

2. 类型和默认值

类型:确保传递给组件的 props 类型正确,Vue 会进行类型检查。

const props = defineProps({

title: String, // 只接受字符串

count: {

type: Number, // 只接受数字

default: 0 // 默认值

}

});

默认值:如果没有提供 props,组件将使用定义的默认值。

const props = defineProps({

count: {

type: Number,

default: 0

}

});

3. 组件的 props 验证

你可以定义自定义验证逻辑:

const props = defineProps({

age: {

type: Number,

validator: value => value >= 18 // 只接受18岁及以上

}

});

4. 注意事项

不可变性:props 是只读的,子组件不能直接修改父组件传递过来的 props。如果需要修改,应该在子组件内部创建一个本地副本:

<script setup>

import { ref, computed } from 'vue';

const props = defineProps(['count']);

const localCount = ref(props.count);

const increment = () => {

localCount.value++;

};

</script>

响应式:如果 props 是对象或数组,确保你在子组件中使用时能够保持其响应性。性能考虑:如果传递的 props是大型数据结构,如数组或对象,确保你的组件能够有效地处理这些数据。避免在子组件中直接进行深度操作,以防止性能问题。组件封装:合理使用 props 有助于组件的封装和复用,保持组件的单一职责和可测试性。

二 自定义事件

自定义事件用于在子组件和父组件之间传递信息

1. 在子组件中触发事件

使用 emit 方法触发自定义事件:

<script setup>

import { defineEmits } from 'vue';

const emit = defineEmits(['my-event']);

const triggerEvent = () => {

emit('my-event', 'Hello from child');

};

</script>

<template>

<button @click="triggerEvent">Trigger Event</button>code>

</template>

2. 在父组件中监听事件

使用 v-on 或简写 @ 监听自定义事件:

<template>

<ChildComponent @my-event="handleMyEvent" />code>

</template>

<script setup>

import ChildComponent from './ChildComponent.vue';

const handleMyEvent = (message) => { -- -->

console.log(message); // 输出 "Hello from child"

};

</script>

3. 事件参数

子组件触发的事件可以带有参数,这些参数会传递给父组件的事件处理函数。可以传递任意类型的数据(如字符串、数字、对象等)。

// 子组件

const triggerEvent = () => {

emit('my-event', 'Hello from child', 123);

};

// 父组件

const handleMyEvent = (message, number) => {

console.log(message, number); // 输出 "Hello from child" 123

};

4 组件解耦

子组件应通过自定义事件向父组件发送消息,而不是直接修改父组件的数据。这样可以保持组件的独立性和可重用性。

<!-- 子组件 -->

<template>

<button @click="notifyParent">Notify Parent</button>code>

</template>

<script setup>

import { -- --> defineEmits } from 'vue';

const emit = defineEmits(['notify']);

const notifyParent = () => {

emit('notify', 'Data');

};

</script>

<!-- 父组件 -->

<template>

<ChildComponent @notify="handleNotify" />code>

</template>

<script setup>

import ChildComponent from './ChildComponent.vue';

const handleNotify = (data) => { -- -->

console.log(data); // 输出 "Data"

};

</script>

三 v-model

在 Vue 3 中,v-model 的原理基于双向数据绑定。其核心是通过 modelValueupdate:modelValue 事件来实现的。具体来说:

父组件:使用 v-model 绑定到子组件的 modelValue 属性。子组件:通过 modelValue 属性接收数据,并在需要时通过 emit('update:modelValue', newValue)触发更新事件,将新值传递回父组件。

在 Vue 3 中,v-model 的默认行为可以被自定义,允许为不同的 props 和事件指定不同的名称。可以通过 v-model:propName@update:propName 的方式来指定。

1 原理

v-model 实现了双向绑定,其底层机制涉及两个主要的部分:

数据绑定:v-model 将父组件的一个数据属性绑定到子组件的 modelValue 属性。事件更新:子组件通过触发 update:modelValue 事件来通知父组件更新数据属性。简化流程示意:

数据绑定:

<ChildComponent v-model="parentValue" />code>

在这个例子中,parentValue 是父组件的数据属性,v-model 将其绑定到ChildComponent 组件的 modelValue 属性上。

数据传递:

<input :value="modelValue" @input="updateValue" />code>

子组件的 input 元素将 modelValue 作为其值,并监听 input 事件。

事件触发:

function updateValue(event) { -- -->

emit('update:modelValue', event.target.value);

}

当用户在输入框中输入内容时,子组件触发 update:modelValue 事件,将新的值传递给父组件。

2 基本用法:

在父组件中使用 v-model 绑定数据到子组件的 props 中,子组件通过 emit 触发更新:

父组件

<template>

<ChildComponent v-model:message="parentMessage" />code>

</template>

<script setup>

import { -- --> ref } from 'vue';

const parentMessage = ref('Hello World');

</script>

子组件

<template>

<input :value="message" @input="$emit('update:message', $event)" />code>

</template>

<script setup>

import { -- --> defineProps, defineEmits } from 'vue';

const props = defineProps(['message']);

const emit = defineEmits();

</script>

3 自定义 v-model:

Vue 3 允许自定义 v-model 的 prop 名称:

父组件

<template>

<CustomComponent v-model:customProp="value" />code>

</template>

<script setup>

import { -- --> ref } from 'vue';

const value = ref('Hello');

</script>

子组件

<template>

<input :value="customProp" @input="$emit('update:customProp', $event)" />code>

</template>

<script setup>

import { -- --> defineProps, defineEmits } from 'vue';

const props = defineProps(['customProp']);

const emit = defineEmits();

</script>

4 多个 v-model:

支持在一个组件上使用多个 v-model:

父组件

<template>

<MultipleModelComponent v-model:title="title" v-model:content="content" />code>

</template>

<script setup>

import { -- --> ref } from 'vue';

const title = ref('Title');

const content = ref('Content');

</script>

子组件

<template>

<input :value="title" @input="$emit('update:title', $event)" />code>

<textarea :value="content" @input="$emit('update:content', $event)" />code>

</template>

<script setup>

import { -- --> defineProps, defineEmits } from 'vue';

const props = defineProps(['title', 'content']);

const emit = defineEmits();

</script>

四 provide 和 inject

在 Vue 3 中,provide 和 inject 是用来在组件层级中共享数据的一种机制。它们特别适用于跨级传递数据,而不需要通过每一级的 props 和事件传递。这种机制适合于较复杂的应用场景,比如主题配置、用户认证状态等。

1 provide 和 inject 的基本用法

1. provide 和 inject 的作用

provide: 在父组件中定义,可以将数据提供给其所有子组件(包括嵌套的子组件)。inject: 在子组件中定义,用于接收从祖先组件提供的数据。

父组件:使用 provide 提供数据

<template>

<div>

<h1>Parent Component</h1>

<child-component />

</div>

</template>

<script setup>

import { provide } from 'vue';

import ChildComponent from './ChildComponent.vue';

const data = 'Shared Data';

provide('sharedData', data); // 提供数据

</script>

在这个父组件中,我们使用 provide 传递了一个名为 ‘sharedData’ 的数据,所有子组件(及其嵌套的子组件)都可以通过 inject 来访问这个数据。

子组件:使用 inject 接收数据

<template>

<div>

<h2>Child Component</h2>

<p>{ { sharedData }}</p>

</div>

</template>

<script setup>

import { inject } from 'vue';

const sharedData = inject('sharedData'); // 接收数据

</script>

在这个子组件中,我们使用 inject 来接收从父组件中提供的数据 ‘sharedData’,并可以在模板中使用它。

2 provide 和 inject 的进阶用法

1. 提供响应式数据

提供响应式数据(如 ref 或 reactive),使得数据在多个组件中保持同步。

<template>

<div>

<h1>Parent Component</h1>

<child-component />

</div>

</template>

<script setup>

import { provide, ref } from 'vue';

import ChildComponent from './ChildComponent.vue';

const count = ref(0);

provide('count', count); // 提供响应式数据

</script>

在子组件中,你可以直接修改和使用这个响应式数据:

<template>

<div>

<h2>Child Component</h2>

<p>Count: { { count }}</p>

<button @click="count++">Increment</button>code>

</div>

</template>

<script setup>

import { -- --> inject } from 'vue';

const count = inject('count'); // 接收响应式数据

</script>

2. 使用 provide 和 inject 进行依赖注入

对于较复杂的应用,可以在多个组件之间共享更复杂的对象或函数。例如,可以用来注入服务或工具函数。

<template>

<div>

<h1>App Component</h1>

<child-component />

</div>

</template>

<script setup>

import { provide } from 'vue';

import ChildComponent from './ChildComponent.vue';

const apiService = {

fetchData() {

return 'Data from API';

}

};

provide('apiService', apiService); // 提供服务

</script>

在子组件中,你可以注入并使用这个服务:

<template>

<div>

<h2>Child Component</h2>

<p>{ { data }}</p>

</div>

</template>

<script setup>

import { inject } from 'vue';

const apiService = inject('apiService');

const data = apiService.fetchData(); // 使用服务

</script>

3 总结

provide: 在父组件中定义,用来提供数据或服务给其所有子组件。inject: 在子组件中定义,用来接收从父组件提供的数据或服务。可以使用响应式数据来确保跨组件的状态同步。适用于复杂的应用程序中需要跨组件传递和共享数据的场景。provide 和 inject 是 Vue 3 强大的功能之一,能够帮助你更灵活地管理组件之间的状态和依赖。

五 refs

$refs 是 Vue.js 提供的一种机制,用于直接访问 DOM 元素或子组件实例。你可以在模板中使用 ref 特性来为 DOM 元素或组件指定一个引用名,然后在 Vue 实例或组件的代码中通过 $refs 来访问这些引用。

在 Vue 3 中,使用 ref 访问子组件时,通常需要通过 expose 来暴露一些方法或数据,以便父组件能够访问。你需要在子组件中使用defineExpose 来明确暴露的内容。这样,父组件就可以直接通过 ref 获取到这些数据或方法

用法

访问 DOM 元素

<template>

<input ref="myInput" type="text" />code>

<button @click="focusInput">Focus Input</button>code>

</template>

<script setup>

import { -- --> ref, onMounted } from 'vue';

const myInput = ref(null);

const focusInput = () => {

myInput.value.focus(); // 访问并操作 DOM 元素

};

onMounted(() => {

console.log(myInput.value); // 在组件挂载后访问 DOM 元素

});

</script>

访问子组件实例

子组件:

在子组件中使用 defineExpose 来暴露你想要的数据或方法。例如:

<template>

<div>{ { internalValue }}</div>

</template>

<script setup>

import { ref, defineExpose } from 'vue';

const internalValue = ref('Hello, World!');

defineExpose({

internalValue

});

</script>

父组件:

在父组件中使用 ref 获取子组件的实例,然后访问暴露的数据或方法:

<template>

<ChildComponent ref="child" />code>

<button @click="showValue">Show Value</button>code>

</template>

<script setup>

import { -- --> ref } from 'vue';

import ChildComponent from './ChildComponent.vue';

const child = ref(null);

const showValue = () => {

alert(child.value.internalValue);

};

</script>

优缺点

优点:

直接访问 DOM 元素或子组件实例,适用于需要直接操作或获取 DOM 元素或组件的方法。可以在组件初始化后执行 DOM 操作,避免了过早访问 DOM 元素的问题。

缺点:

过度使用 ref 可能会使组件的耦合度变高,使组件之间的关系变得复杂。不应过多依赖 ref 来解决问题,应该优先考虑使用 props 和 events 进行数据传递和事件处理。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。