《组件间的信息桥梁:探索组件通信之道》

139 阅读6分钟

《组件间的信息桥梁:探索组件通信之道》

组件通信是指在一个应用中,不同组件之间进行数据交换和信息传递的过程。

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 中,组件通信是指组件之间进行数据传递和交互的方式,主要包括以下几种常见的方法:

  1. 父组件向子组件传递数据(Props) :父组件通过在子组件标签上设置属性,将数据传递给子组件。子组件通过 defineProps 接收父组件传来的数据,但应遵循单向数据流原则,避免直接修改父组件传递的 props 数据。

  2. 子组件向父组件通信(自定义事件) :子组件通过 defineEmits 定义自定义事件,使用 emit 函数触发事件,并向父组件传递数据。父组件在使用子组件时,通过 v-on 或 @ 监听子组件触发的自定义事件,并在相应的处理函数中接收数据。

  3. 父组件获取子组件实例或数据(ref :父组件可以在子组件上使用 ref 属性来获取子组件的实例引用,但直接通过 ref 获取子组件内部数据的方式不太推荐,更好的方式是通过上述的属性传递和自定义事件来进行通信。

  4. 全局状态管理(如 Vuex) :对于复杂的应用,多个组件可能需要共享和管理全局状态,此时可以使用 Vuex 等状态管理库来集中管理和处理组件之间的共享数据。

  5. Provide / Inject:父组件可以使用 provide 向子孙组件提供数据,子孙组件通过 inject 来获取这些数据。但应注意提供和获取的数据应该是响应式的对象,而不是对象的某个属性值。

良好的组件通信设计有助于提高代码的可维护性、可扩展性和复用性,使组件之间的关系更加清晰和易于理解。在实际开发中,应根据具体的业务需求和项目结构选择合适的组件通信方式。