Vue的组件通信

发布于:2024-07-02 ⋅ 阅读:(154) ⋅ 点赞:(0)

前言

今天讲讲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 和一个“添加”按钮。
    • 定义了 valuelist 两个响应式变量。
    • 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 元素可能需要一些时间才能完全渲染。如果在父组件的钩子函数中直接访问子组件的属性或方法,可能会导致获取到不完整或错误的信息。

总结

本文讲解了组件通信中的父子组件通信和子父组件通信,希望看到这里的你能够有所收获!!!


网站公告

今日签到

点亮在社区的每一天
去签到