章节前言
Vue.js 的设计鼓励组件之间的解耦和数据流的清晰性。本章旨在全面覆盖 Vue 3 中的各种通信方式,帮助读者更好地理解和选择最适合他们项目的通信策略。
vue3通信大全(二)—— 路由传参与 teleport - 掘金 (juejin.cn)
vue3通信大全(三)—— 全局状态管理库pinia,vuex - 掘金 (juejin.cn)
vue3通信大全(四)—— Slot /浏览器储存/window - 掘金 (juejin.cn)
前言
在 Vue 3 中,组件间通信是构建可维护且灵活的应用程序的关键。随着应用程序变得越来越复杂,组件之间有效地共享数据和状态变得至关重要。Vue 3 提供了多种机制来实现这一点,从简单的父子组件通信到更为复杂的跨组件通信。
父子组件通信 defineProps
在 Vue 3 中,父组件向子组件传递数据通常通过 props
实现。使用 defineProps
选项来声明子组件接收的 props
。
父组件:
<template>
<Child :message="message"/>
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const message=ref('父类的数据')
</script>
子组件:
<template>
<div>
<p>{{message}}</p>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props=defineProps({
message: {
type: String,
required: true
}
})
</script>
当我们希望子组件能够修改父组件的数据时,有下面两种思路:
1.传递修改函数(Prop + Callback)
父组件:
<template>
<Child :message="message" :updateMessage="updateMessage"/> //传递修改方法
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const message=ref('父类的数据')
function updateMessage() {
message.value = '修改后父类的数据';
}
</script>
子组件:
<template>
<div>
<p>{{message}}</p>
<button @click="updateMessage">Change</button>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props=defineProps({
message: {
type: String,
required: true
},
updateMessage:{
type: Function,
required: true
}
})
</script>
2.子组件修改后再通知父组件(Event Emitter)
子组件能够修改父组件的数据时,可以通过定义一个自定义事件并在父组件中监听该事件来实现这一目的。 子组件:
<template>
<div>
<p>{{message}}</p>
<button @click="updateMessage">Change</button>
</div>
</template>
<script setup>
import { defineProps , defineEmits } from 'vue';
const props=defineProps({
message: {
type: String,
required: true
},
})
// 定义一个自定义事件 update,并在父组件中通过 v-on:update 接收并处理
const emits = defineEmits(['update']); //定义事件
const updateMessage = () => {
emits('update', '修改后父类的数据'); //发布事件
}
</script>
父组件:
<template>
<Child :message="message" @update="changeMsg"/> //监听(订阅)事件
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const message=ref('父类的数据')
function changeMsg(value) {
message.value = value;
}
</script>
除了上面这个方法外,也可以使用v-model
绑定子组件。父组件通过 v-model
绑定一个属性到子组件,而子组件则通过触发一个特定的事件来通知父组件数据已经更新。这里的关键在于子组件需要触发一个名为 update:modelValue
的事件,这样 Vue 才能识别这是一个 v-model
的更新事件,并自动更新父组件中的数据。
父组件:
<template>
<Child :message="message" v-model="message"/>
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const message=ref('父类的数据')
</script>
子组件:
<template>
<div>
<p>{{message}}</p>
<button @click="updateMessage">Change</button>
</div>
</template>
<script setup>
import { defineProps , defineEmits } from 'vue';
const props=defineProps({
message: {
type: String,
required: true
},
})
const emits = defineEmits(['update:modelValue']);
const updateMessage = () => {
emits('update:modelValue', '修改后父类的数据');
}
</script>
如果想要自己使用自定义事件名称,需要在 v-model
中添加一个 :event
修饰符,并指定你想要使用的事件名称。
父组件:
<Child :message="message" v-model:changeMsg="message"/>
子组件:
const emits = defineEmits(['update:changeMsg']);
const updateMessage = () => {
emits('update:changeMsg', '修改后父类的数据');
}
子父通信 defineEmits
当我们希望子组件能够将数据传递给父组件时,通常的做法是通过子组件触发事件使用 defineEmits
来通知父组件。这里我们切记使用defineExpose
暴露给父组件的数据是不会在父组件里面实时更新的
子组件:
<template>
<div>
<h2>子组件</h2>
<p>消息:{{ message }}</p>
<button @click="updateMessage">change</button>
</div>
</template>
<script setup>
import { ref,defineExpose } from 'vue'
const message=ref('子类的数据')
function updateMessage() {
message.value = '子组件的消息已更改';
}
defineExpose({ message, updateMessage });
</script>
父组件:
<template>
<Child ref="childRef" />
<div>
<h2>父组件</h2>
<p>消息:{{parentMessage }}</p>
<button @click="updateFromChild">childeMsg</button>
</div>
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const parentMessage = ref('父组件的初始消息');
const childRef = ref(null);
function updateFromChild() {
parentMessage.value = childRef.value.message;
}
</script>
跨级通信 provide && inject
provide
和 inject
是一种在组件树中进行跨层级通信的方式。它们允许你将数据从祖先组件传递给其后代组件,而无需通过中间组件逐层传递 props。这种方式特别适合于那些需要被多个组件访问的数据或方法,而且这些组件之间没有直接的父子关系。
provide/inject比较简单,声明provide(键名, 键值)
,注入:inject(键名)
返回出键值
顶级组件:
<template>
<div>
<h1>Top Component</h1>
<middle-component />
</div>
</template>
<script setup>
import MiddleComponent from './Middle.vue';
const topData = 'Data from the top component';
provide('topData', topData);
</script>
中间组件:
<template>
<div>
<h2>Middle Component</h2>
<leaf-component />
</div>
</template>
<script setup>
import LeafComponent from './Leaf.vue';
</script>
叶子组件:
<template>
<div>
<h3>Leaf Component</h3>
<p>Data from top: {{ topData }}</p>
</div>
</template>
<script setup>
import { inject } from 'vue';
const topData = inject('topData');
</script>