前言
今天讲讲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 元素可能需要一些时间才能完全渲染。如果在父组件的钩子函数中直接访问子组件的属性或方法,可能会导致获取到不完整或错误的信息。
总结
本文讲解了组件通信中的父子组件通信和子父组件通信,希望看到这里的你能够有所收获!!!