《组件间的信息桥梁:探索组件通信之道》
组件通信是指在一个应用中,不同组件之间进行数据交换和信息传递的过程。
1.父子通信 --- 父组件传值,子组件通过 defineProps
parent.vue --- 父组件
<template>
<div class="header">
<input type="text" v-model="newMsg">
<button @click="add">确定</button>
</div>
<child :value="list"></child>
</template>
<script setup>
import { ref } from 'vue';
import child from './child.vue';
const newMsg = ref('')
const list = ref(['html', 'css'])
const add = () => {
list.value.push(newMsg.value)
}
</script>
<style lang="css" scoped></style>
将newMsg ,list变成响应式,并且将newMsg跟输入框进行绑定,实时获取输入框的值,提交按钮绑定一个点击事件,将输入框的值添加到list数组里面,:value="list"进行数据绑定,传输给子组件。
child.vue --- 子组件
<template>
<div class="body">
<ul>
<li v-for="item in value">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
defineProps({
value: {
type: Array,
default: () => []
}
});
/*
- `value` 是定义的属性名称。
- `type: Array` 表示这个属性 `value` 的类型是数组。
- `default: () => []` 为这个属性提供了一个默认值。当父组件没有传递 `value` 属性时,该组件内的 `value` 属性将使用默认的空数组 `[]` 。
*/
</script>
<style lang="css" scoped></style>
子组件通过defineProps来接收父子件传来的值, v-for="item in value",通过for循环将数组中的值显示在页面上。
2.父子通信 --- 父组件传值,子组件监听
父组件传值到子组件,子组件监听响应式defineProps,如有变化,用监听方法将新值放入响应式数组中,在页面打印。
parent.vue --- 父组件
<template>
<div class="header">
<input type="text" v-model="newMsg">
<button @click="add">确定</button>
</div>
<child :value="value"></child>
</template>
<script setup>
import { ref } from 'vue';
import child from './child.vue';
const newMsg = ref('')
const value = ref('')
const add = () => {
value.value = newMsg.value
}
</script>
<style lang="css" scoped></style>
将newMsg ,list变成响应式,并且将newMsg跟输入框进行绑定,实时获取输入框的值,提交按钮绑定一个点击事件,将输入框的值添加到value,:value="value"进行数据绑定,传输给子组件。
child.vue --- 子组件
<template>
<div class="body">
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { computed, onBeforeUpdate, onUpdated, ref } from 'vue';
const list = ref(['html', 'css'])
const props = defineProps({
value: ''
});
//方法一
const list2 = computed(() => {
list.value.push(props.value)
return [...list.value];
});
//方法二
const watch = (
() => props.value,
(newVal, oldVal) => {
if (newVal) {
list.value.push(newVal)
}
}
);
//方法三
onBeforeUpdate(() => {
list.value.push(props.value)
})
//方法四
onUpdated(() => {
list.value.push(props.value)
})
</script>
<style lang="css" scoped></style>
提供了四种方法,我将为大家一一讲解,
子组件通过defineProps来接收父子件传来的值
方法一:
//方法一
const list2 = computed(() => {
list.value.push(props.value)
return [...list.value];
});
使用计算属性computed,当里面的响应式defineProps里面的数据局改变时,会自动执行里面的方法,将传进来的值,加入到list数组中, v-for="item in list",将list数组循环到页面上。
方法二:
//方法二
const watch = (
() => props.value,
(newVal, oldVal) => {
if (newVal) {
list.value.push(newVal)
}
}
);
使用watch来监听props.value的变化,如果props.value变化的话,回调函数就会被触发,将新值newVal添加到list中
方法三:
//方法三
onBeforeUpdate(() => {
list.value.push(props.value)
})
onBeforeUpdate 钩子函数。
onBeforeUpdate 钩子会在组件即将重新更新之前被调用。
在父组件中修改了传递给子组件的 props 的值,Vue 会自动将新的值传递给子组件,并更新子组件的页面展示。
当组件即将更新时,会将 props.value 的值添加到 list.value 中。
方法四:
//方法四
onUpdated(() => {
list.value.push(props.value)
})
使用了 onUpdated 钩子函数。当组件完成更新后,这个钩子函数中的代码会被执行,
会将 props.value 的值推送到 list.value 中,此时不会再重新更新页面,而是对改变的数据进行改变。
3.父子通信 --- 父组件直接传值给子组件
parent.vue --- 父组件
<template>
<div class="header">
<input type="text" v-model="newMsg">
<button @click="add">确定</button>
</div>
<child></child>
</template>
<script setup>
import child from "./child.vue";
import { ref, provide } from 'vue';
const newMsg = ref('')
const list = ref(['html', 'css'])
const add = () => {
list.value.push(newMsg.value)
}
provide('list', list)
</script>
<style lang="css" scoped></style>
v-model="newMsg",输入框数据跟newMsg进行绑定,将确定按钮绑定add事件,点击确定,触发add事件,将输入框的值加入到list,通过provide('list', list),向子组件传输list。
child.vue --- 子组件
<template>
<div class="body">
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { inject } from 'vue'
const list = inject('list')
</script>
<style lang="scss" scoped></style>
子组件通过inject('list'),接收父组件传来的list,并且打印到页面上
4.子父通信 --- 通过发布订阅机制
父组件订阅一个事件,子组件发布该事件且将要传递的值一起发出来,父组件在定于函数中获取该值。
parent.vue --- 父组件
<template>
<child @addMsg="handle"></child>
<div class="body">
<ul>
<li v-for="item in value">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue';
import child from './child.vue';
const list = ref(['html', 'css'])
const handle = (e) => {
console.log(e);
list.value.push(e)
}
</script>
<style lang="css" scoped></style>
@addMsg="handle",订阅addMSg函数,一旦函数发布,执行handle函数,将addMsg传来的值加入到list中,并且打印到页面。
child.vue --- 子组件
<template>
<div class="header">
<input type="text" v-model="newMsg">
<button @click="add">确定</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const newMsg = ref('')
const emist = defineEmits(['addMsg'])
const add = () => {
emist('addMsg', newMsg.value)
}
</script>
<style lang="css" scoped></style>
v-model,将输入框和newMsg进行双向绑定
自定义了addMsg函数
点击确定,执行add函数,发布addMsg函数并带上newMsg的值。
5.子父通信 --- 父组件 v-model 绑定属性传给子组件
子组件发布 update:xxx 事件通知父组件数据更新了
parent.vue --- 父组件
<template>
<child v-model:list="list"></child>
<div class="body">
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue';
import child from './child.vue';
const list = ref(['html', 'css'])
</script>
<style lang="css" scoped></style>
父组件通过v-model:list="list",将子父组的list进行双向绑定,以保持数据的同步。之所以子组件不直接修改,因为准守着子组件不直接修改父组件的值。
child.vue --- 子组件
<template>
<div class="header">
<input type="text" v-model="newMsg">
<button @click="add">确定</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
list: []
})
const newMsg = ref('')
const emit = defineEmits(['update:list'])
const add = () => {
const arr = props.list
arr.push(newMsg.value)
emit('update:list', arr)
}
</script>
<style lang="css" scoped></style>
v-model="newMsg",将输入框的值跟newMsg进行绑定
确定按钮绑定add点击事件
子组件自定义update:list事件
add事件被触发,将原list值赋值给新数组,并将输入框的值加入到新数组中,并通过 emit('update:list', arr) 触发自定义的 update:list 事件,将修改后的数组传递给父组件。
总结
在 Vue 中,组件通信是指组件之间进行数据传递和交互的方式,主要包括以下几种常见的方法:
-
父组件向子组件传递数据(Props) :父组件通过在子组件标签上设置属性,将数据传递给子组件。子组件通过
defineProps接收父组件传来的数据,但应遵循单向数据流原则,避免直接修改父组件传递的props数据。 -
子组件向父组件通信(自定义事件) :子组件通过
defineEmits定义自定义事件,使用emit函数触发事件,并向父组件传递数据。父组件在使用子组件时,通过v-on或@监听子组件触发的自定义事件,并在相应的处理函数中接收数据。 -
父组件获取子组件实例或数据(
ref) :父组件可以在子组件上使用ref属性来获取子组件的实例引用,但直接通过ref获取子组件内部数据的方式不太推荐,更好的方式是通过上述的属性传递和自定义事件来进行通信。 -
全局状态管理(如 Vuex) :对于复杂的应用,多个组件可能需要共享和管理全局状态,此时可以使用 Vuex 等状态管理库来集中管理和处理组件之间的共享数据。
-
Provide / Inject:父组件可以使用
provide向子孙组件提供数据,子孙组件通过inject来获取这些数据。但应注意提供和获取的数据应该是响应式的对象,而不是对象的某个属性值。
良好的组件通信设计有助于提高代码的可维护性、可扩展性和复用性,使组件之间的关系更加清晰和易于理解。在实际开发中,应根据具体的业务需求和项目结构选择合适的组件通信方式。