前言
今天讲讲Vue的组件通信的几种方式
正文
父子通讯 – 父给子
父组件将值 v-bind 绑定传给子组件,子组件通过 degineProps 接收
接下来用一个代码示例讲解一下
-
父组件
value
是使用ref
创建的响应式变量,用于绑定输入框的值。to
也是使用ref
创建的响应式变量,用于向子组件传递数据。add
方法在点击“添加”按钮时被触发。如果value
的值不为空,就将其赋值给to
。- 在模板中,有一个输入框
v-model
绑定了value
,一个“添加”按钮绑定了add
方法,还使用<Child :msg="to" />
将to
的值传递给子组件Child
。
<template><div class="input-group"><input type="text" v-model="value" /><button @click="add">添加</button></div><Child :msg="to"></Child>
</template><script setup>
import { ref } from "vue";
import Child from "@/components/child.vue";const value = ref("");
const to = ref('')
const add = () => {if (value.value !== "") {to.value = value.value ;}
};
</script><style lang="css" scoped></style>
-
子组件:
list
是使用ref
创建的响应式数组,初始值包含"html"
、"css"
、"js"
。- 使用
defineProps
接收来自父组件的msg
属性。 - 直接将
props.msg
添加到list
数组中。 - 使用
watch
监听props.msg
的变化。当props.msg
发生变化时,将新值添加到list
数组中,并在控制台打印新值和旧值。
<template><div class="child"><ul><li v-for="item in list">{{ item }}</li></ul></div>
</template><script setup>
import { ref, watch } from "vue";
const list = ref(["html", "css", "js"]);const props = defineProps({msg: "",
});
list.value.push(props.msg);watch(() => props.msg,(newVal, oidVal) => {console.log(newVal, oidVal);list.value.push(newVal);}
);
</script><style lang="css" scoped></style>
子父通讯 – 子给父
第一种方式(发布订阅)
借助发布订阅机制,子组件通过发布并携带参数,父组件订阅事件通过事件提供的函数获取值
- 父组件:
- 引入了子组件
Child
,并通过@add1="handle"
监听子组件触发的add1
事件。 - 定义了一个响应式数组
list
,初始值包含"html"
、"css"
、"js"
。 handle
函数用于处理接收到的add1
事件,将事件传递的数据添加到list
数组中。
- 引入了子组件
<template><!-- 订阅add1事件 --><Child @add1="handle"></Child><div class="child"><ul><li v-for="item in list">{{ item }}</li></ul></div>
</template><script setup>
import { ref } from "vue";
import Child from "./components/child2.vue";const list = ref(["html", "css", "js"]);const handle = (event) => {list.value.push(event);
};
</script><style lang="css" scoped></style>
- 子组件
- 定义了一个输入框
v-model
绑定了value
,以及一个“添加”按钮。 - 使用
defineEmits
创建了一个名为add1
的自定义事件。 add
方法在点击“添加”按钮时被触发,通过emit("add1", value.value)
向父组件触发add1
事件,并将value
的值作为参数传递给父组件。
- 定义了一个输入框
<template><div class="input-group"><input type="text" v-model="value" /><button @click="add">添加</button></div>
</template><script setup>
import { ref } from "vue";const value = ref("");const emit = defineEmits(["add1"]); // 创建一个add事件
const add = () => {// 将数据传递给父组件emit("add1", value.value); // 第一个参数是事件名,第二个参数是要传递的数据 发布事件
};
</script><style lang="css" scoped></style>
第二种方式(v-model)
子父组件通讯 父组件借助 v-model 将数据绑定给子组件,子组件创建"update:XXX"事件,并接收到数据修改后 emits 出来
-
父组件
- 在模板中使用
<Child v-model:list="list" />
,通过v-model
语法与子组件进行数据交互,将父组件中的list
数据传递给子组件,并能接收子组件对list
的修改。 - 利用
ref
创建了名为list
的响应式数组,其初始值为["html", "css", "js"]
,并在模板中通过v-for
遍历展示其元素。
- 在模板中使用
<template><Child v-model:list="list"></Child><div class="child"><ul><li v-for="item in list">{{ item }}</li></ul></div>
</template><script setup>
import { ref } from "vue";
import Child from "./components/child3.vue";const list = ref(["html", "css", "js"]);
</script><style lang="css" scoped></style>
- 子组件
- 模板中有一个输入框
v-model
绑定了value
和一个点击触发add
方法的“添加”按钮。 - 通过
defineProps
定义了从父组件接收的list
属性,指定其类型为数组,并提供了默认的空数组。 - 利用
defineEmits
定义了名为update:list
的自定义事件,用于向父组件传递数据更新。 - 在
add
方法中,为了避免直接修改父组件传来的props.list
,先复制了一份到arr
,然后向arr
中添加新值,最后通过触发Emits("update:list", arr)
事件将更新后的数组传递回父组件,实现与父组件的双向数据绑定。这种复制后修改再传递的方式,是为了遵循 Vue 的单向数据流原则,保证数据的准确性和可预测性。
- 模板中有一个输入框
<template><div class="input-group"><input type="text" v-model="value" /><button @click="add">添加</button></div>
</template><script setup>
import { ref } from "vue";const value = ref("");const props = defineProps({list: {type: Array,default: () => [],},
});const emits = defineEmits(["update:list"]);
const add = () => {// props.list.push(value.value);// 不建议直接操作父组件给过来的数据const arr = props.list;arr.push(value.value);emits("update:list", arr);
};
</script><style lang="css" scoped></style>
-
父组件
- 引入子组件
Child
,并通过ref="childRef"
为子组件设置引用。 - 在模板中,尝试通过
childRef?.list
访问子组件暴露出来的list
属性,并使用v-for
进行遍历展示。 - 使用
ref
创建了childRef
,初始值为null
。 - 在
onMounted
钩子函数中,打印childRef.value
,即在组件挂载后获取子组件的引用。
- 引入子组件
<template><Child ref="childRef"></Child><div class="child"><ul><li v-for="item in childRef?.list">{{ item }}</li></ul></div>
</template><script setup>
import { ref, onMounted } from "vue";
import Child from "./components/child4.vue";const childRef = ref(null);onMounted(() => {console.log(childRef.value);
});
</script><style lang="css" scoped></style>
- 子组件
- 包含一个输入框
v-model
绑定了value
和一个“添加”按钮。 - 定义了
value
和list
两个响应式变量。 add
方法用于向list
中添加新值。- 通过
defineExpose({ list })
主动暴露list
属性,使得父组件能够通过引用获取到。
- 包含一个输入框
<template><div class="input-group"><input type="text" v-model="value" /><button @click="add">添加</button></div>
</template><script setup>
import { ref } from "vue";const value = ref("");
const list = ref(["html", "css", "js"]);const add = () => {list.value.push(value.value);
};defineExpose({ list }); // 心甘情愿暴露出来
</script><style lang="css" scoped></style>
这里使用childRef?的原因:
ref
用于注册引用信息,可以通过 ref
获取 DOM 元素或组件实例。在父组件中,使用 ref
为子组件创建一个引用,在模板中可以通过 ref
访问子组件的属性和方法。
然而,在访问子组件的属性或方法时,可能会存在子组件尚未完全渲染的情况。由于 Vue 的异步渲染策略,子组件的 DOM 元素可能需要一些时间才能完全渲染。如果在父组件的钩子函数中直接访问子组件的属性或方法,可能会导致获取到不完整或错误的信息。
总结
本文讲解了组件通信中的父子组件通信和子父组件通信,希望看到这里的你能够有所收获!!!