一、父传子(props)
关于Props的相关内容可以参考:Props-CSDN博客
父组件通过 props 向子组件传递数据。适合简单的单向数据流。
<!-- Parent.vue -->
<template>
<Child :message="parentMessage" />
</template>
<script setup>
const parentMessage = ref('Hello')
</script>
- 在父组件中,通过 :message 语法将 parentMessage 传递给子组件 Child。
<!-- Child.vue -->
<template>
<div>{{ props.message }}</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
message: {
type: String,
required: true // 指定该 prop 为必需
}
});
</script>
- 在子组件 Child 中,使用 props 选项定义接收的 message 属性
- type 用于指定 props 的类型(如 String、Number 等)
- required 用于指示该 prop 是否为必需
二、子传父(自定义事件)
子组件可以通过 $emit 向父组件发送事件,父组件通过v-on监听这些事件来接收数据
<!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
<Event2 @xxx="handler3"></Event2> //handler3是一个你想操作的方法
- 父组件中接收自定义事件
<button @click="sendMessage">点击触发子传父</button>
let $emit = defineEmits(['xxx']); //利用defineEmits方法返回函数触发自定义事件
sendMessage() {
$emit('childMessage', 'Hello from Child!');
}
- 子组件中:点击按钮触发“向父组件传递信息”的操作
三、v-model组件通信(父子组件数据同步)
vue3.4之前:
1、前言:通过 v-model,父组件可以将数据传递给子组件,子组件可以在内部修改这些数据,并将更新后的值同步回父组件
2、使用 v-model 实现数据同步的步骤:
- 父组件传递数据:父组件使用 v-model 将数据传递给子组件。
- 子组件定义 modelValue:子组件需要定义 modelValue 属性来接收来自父组件的数据。
- 子组件触发更新:子组件通过 $emit 触发 update:modelValue 事件,将修改后的数据发送回父组件。
3、代码示范:
//父组件中
<Child v-model:value="value" />
//子组件中
<template>
<input v-model="inputValue" @input="updateValue" /> //@input 是一个原生 DOM 事件,表示用户在输入框中输入内容时触发的事件。
</template>
<script setup lang="ts">
import { defineProps, defineEmits, ref, watch } from 'vue';
// 定义 props,并接收父组件传递的值
const props = defineProps<{
modelValue: string;
}>();
// 定义事件
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void;
}>();
// 创建响应式变量
const inputValue = ref(props.modelValue);
// 监听 props 的变化,确保 inputValue 始终与 modelValue 同步
watch(
() => props.modelValue,
(newValue) => {
inputValue.value = newValue;
}
);
// 更新值并触发更新事件
const updateValue = () => {
emit('update:modelValue', inputValue.value);
};
</script>
注:其主要实现原理还是利用了props和自定义事件的组合使用,v-model只是帮我们同步了数据和方法
vue3.4之后(使用 defineModel())
1、defineModel():
该函数简化了 v-model 的使用,自动处理 modelValue 的接收和 update:modelValue 事件的发出。
2、使用:
<!-- Parent.vue -->
<Child v-model="countModel" />
- 在父组件中,使用 v-model 将 countModel 绑定到子组件 Child 的 model(这是 defineModel() 中定义的)。
- 当父组件的 countModel 变化时,子组件将自动接收到更新。
<!-- Child.vue -->
<script setup>
const model = defineModel()
function update() {
model.value++
}
</script>
<template>
<div>Parent bound v-model is: {{ model }}</div>
<button @click="update">Increment</button>
</template>
- defineModel() 函数用于定义 v-model 的数据绑定。在这里,它会自动接收来自父组件的 countModel 的值,并将其存储在 model 中。
- model 变量是响应式的,因此,当父组件的 countModel 发生变化时,model 也会更新。
3、数据流动
从父组件到子组件:父组件的 countModel 值通过 v-model 传递给子组件,并被 defineModel() 接收并存储在 model 中。
从子组件到父组件:
- 当用户在子组件中点击按钮,update() 方法会增加 model.value 的值。
- -由于 defineModel() 自动处理了 update:modelValue 事件,这样子组件在更新 model.value 时,会自动触发父组件 countModel 的更新。
4、v-model的参数
组件上的v-model也可以接受一个参数:
<MyComponent v-model:title="bookTitle" />
在子组件中,通过将字符串作为第一个参数传递给defineModel()来支持相应的参数:
<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>
<template>
<input type="text" v-model="title" />
</template>
5、多个v-model绑定
组件上的每一个v-model都会同步不同的prop,而无需额外的选项:
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
<!-- Username.vue -->
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>
6、处理 v-model 修饰符
想创建一个自定义的修饰符来实现某种功能,例:将 v-model 绑定输入的字符串值第一个字母转为大写
<MyComponent v-model.capitalize="myText" />
为了能够基于修饰符选择性地调节值的读取和写入方式,我们可以给 defineModel() 传入 get 和 set 这两个选项。
<script setup>
const [model, modifiers] = defineModel({
set(value) {
if (modifiers.capitalize) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
return value
}
})
</script>
<template>
<input type="text" v-model="model" />
</template>
7、带参数的v-model修饰符
这里用例子展示了如何在使用多个不同参数的v-model时使用修饰符:
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>
<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')
console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true }
</script>
总结
Vue 3.4 后,使用 v-model 进行组件通信的方式更加简洁和高效,提升了代码的可读性和维护性。
四、全局事件总线(mitt)
背景:在 Vue 3 中,虽然不再推荐使用全局事件总线(Event Bus)作为组件之间通信的主要方式,但仍然可以通过一些方法实现类似的功能。全局事件总线的主要目的是在不同组件之间传递事件,而不通过父子组件的直接通信。
使用:
1、安装 mitt:
mitt 是一个轻量级的事件总线库,可以方便地在 Vue 3 中使用。
npm install mitt
2、创建事件总线:
// eventBus.js
import mitt from 'mitt';
const eventBus = mitt();
export default eventBus;
3、在组件中使用事件总线:
<!-- ComponentA.vue -->
<script setup lang="ts">
import eventBus from './eventBus';
const sendMessage = () => {
eventBus.emit('xxx', 'Hello from Component A!');
};
</script>
<!-- ComponentB.vue -->
<script setup lang="ts">
import {onMounted, onUnmounted } from 'vue';
import eventBus from './eventBus';
const receiveMessage = (msg: string) => {
message.value = msg;
};
//在事件总线中注册一个事件监听器,以便在特定事件发生时执行相应的回调函数
onMounted(() => {
eventBus.on('xxx', receiveMessage);
});
//清理事件监听,避免内存泄漏
onUnmounted(() => {
eventBus.off('xxx', receiveMessage);
});
</script>
五、使用 provide/inject(隔辈通信)
使用props实现逐级透传 VS 使用provide-inject
VS
provide() 函数:为组件后代提供数据
<script setup>
import { provide } from 'vue'
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
</script>
注:如果不使用 <script setup>,请确保 provide() 是在 setup() 同步调用的
参数:
- 注入名:后代组件用注入名来查找期望注入的值;类型:字符串/Symbol
- 提供的值:可以是任意类型的,包括响应式的状态
inject:注入上层组件提供的数据
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
注:如果提供的值是一个 ref,注入进来的会是该 ref 对象,而不会自动解包为其内部的值。这使得注入方组件能够通过 ref 对象保持了和供给方的响应性链接。
六、useAttrs组件通信
1、useAttrs 的基本概念:
- 用途:useAttrs 允许子组件访问和操作父组件传递的属性和事件,而无需显式地定义这些属性。
- 场景:常用于构建高度可复用和灵活的组件,比如包装器组件或 UI 库组件。
2、基本用法:
<!-- MyButton.vue -->
<template>
<button v-bind="attrs">
</button>
</template>
<script setup>
import { useAttrs } from 'vue';
const attrs = useAttrs(); // 获取父组件传递的属性
</script>
<!-- Parent.vue -->
<template>
<MyButton type="submit" class="btn" @click="handleSubmit">提交 </MyButton>
</template>
type="submit" class="btn"是传递的属性:子组件可以通过 useAttrs 访问这些属性,并将它们应用于其内部的 DOM 元素
@click="handleSubmit":这是一个事件监听器,当子组件中的按钮被点击时,会调用父组件中的 handleSubmit 方法。
3、详细解释:
访问属性:
useAttrs():
- 返回一个 Reactive 对象,包含所有传递给当前组件的非响应式属性。
- 这些属性可以使用 v-bind 绑定到子组件的 DOM 元素上。
事件处理:
事件传递:
- 子组件可以直接使用 @eventName 语法捕获父组件传递的事件。
- 此外,子组件可以将事件处理逻辑封装在自己的方法中,进一步控制事件的处理。
4、总结
useAttrs 只获取非响应式属性。如果需要响应式数据,仍需使用 props。
七、Pinia
pinia的有关知识可以去参考:pinia(vue3)-CSDN博客
Pinia 是一个用于状态管理的库,通常用于替代 Vuex。它提供了一种简单且直观的方式来管理应用的全局状态,并支持组件之间的通信。
八、ref与$parent
- ref在父组件中获取子组件实例对象
- $parent可以在子组件内部获取父组件实例对象
以下的示例是从从父组件中控制子组件:
<template>
<div>
<h1>父组件计数: {{ count }}</h1>
<Child ref="child" />
<button @click="increment">增加</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const count = ref(0); // 父组件的响应式数据
const child = ref(null); // 用于存储子组件的引用
const increment = () => {
count.value++; // 增加父组件的计数
};
// 可以通过子组件的方法来更新父组件的状态
const updateFromChild = () => {
// 调用子组件的某个方法
child.value.updateCount();
};
</script>
子组件:
<template>
<div>
<h2>子组件</h2>
</div>
</template>
<script setup>
const updateCount = () => {
console.log('更新')
}
</script>
总结:
在 Vue 中,使用 ref 和$parent 是一种实现组件通信的方式,虽然这种方式在某些情况下有效,但通常不建议使用,因为它可能导致代码的耦合性增加和可维护性降低。