Vue中父子组件通讯

前言

组件通讯是指在一个使用组件化架构的前端框架中,不同组件之间按照层次关系进行的数据交换。在Vue中,当一个组件(父组件)包含另一个组件(子组件)时,它们可以通过特定的机制相互传递数据和消息。

代码部分这里我们使用类似于todoList的页面作为示例:

我们在input框中输入后点击添加按钮,输入的内容会添加到list下方

父子组件通讯

父组件将值v-bind绑定传给子组件,子组件使用defineProps接收

打个比方:

父子组件通讯就好比家长和孩子之间的对话。

家长(父组件)可以给孩子(子组件)一些东西,比如零花钱(数据),这是通过props实现的。

而孩子如果需要什么,比如想买玩具,就会向家长喊一声(触发事件),家长听到后就可以决定是否满足孩子的需求(处理事件)。

这样一来,尽管家长和孩子(组件)各自独立,但他们之间还是能很好地交流和互动。

代码:

父组件:

js 复制代码
<template>
  <!-- 输入框和按钮 -->
  <div class="input-group">
    <!-- 输入框,其值与value响应式变量双向绑定 -->
    <input type="text" v-model="value">
    <!-- 绑定点击事件 -->
    <button @click="add">添加</button>
 </div>
 <!-- 子组件,接收toChild的值作为msg属性 -->
  <Child :msg="toChild"></Child>
</template>

<script setup>
// 导入子组件
import Child from '@/components/child.vue'
// 导入Vue的ref响应式引用
import { ref } from 'vue'

// 定义响应式变量,用于存储输入框的值
const value = ref('')
// 定义响应式变量,用于存储要传递给子组件的值
const toChild = ref('')

// 定义add函数,当按钮被点击时执行
const add = () => {
  // 将输入框的值赋给toChild
  toChild.value = value.value
}
</script>

<style lang="css" scoped></style>

子组件:

js 复制代码
<template>
    <!-- 显示列表 -->
  <div class="child">
    <ul>
      <!-- 遍历list数组,显示每一个元素 -->
      <li v-for="item in list">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
// 导入Vue的ref和watch
import { ref, watch } from 'vue'
// 初始化list数组
const list = ref(['html', 'css', 'js'])

// 定义props,接收父组件传递的msg属性
const props = defineProps({
  msg: ''
})

// 监听msg属性的变化
watch(
  // 返回一个计算属性,表示msg的值
  () => props.msg,
  // 当msg发生变化时执行的回调函数
  (newVal, oldVal) => {
    // 将新的msg值添加到list数组中
    list.value.push(newVal)
  }
)

</script>

<!-- css没有东西 -->
<style lang="css" scoped></style>

当父组件的add函数被调用时,它会将输入框的值赋给toChild,这个值随后通过v-bind传递给子组件的msg属性。

由于子组件使用watch监听了msg的变化,因此每当msg更新,新值都会自动追加到list数组中,从而更新UI显示。

子父组件通讯

1. 发布订阅

1. 借助发布订阅机制,子组件负责发布事件并携带参数,父组件订阅该事件通过事件获取子组件提供的值

2. 子组件触发添加含有参数的分布事件,父组件添加订阅事件读取该值

父组件:

js 复制代码
<template>
  <!-- 显示列表 -->
  <div class="child">
    <ul>
      <!-- 遍历list数组,显示每一个元素 -->
      <li v-for="item in list">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
// 导入子组件Child,这里我的子组件文件名为child2.vue
import Child from "@/components/child2.vue";
// 导入Vue的ref响应式引用
import { ref } from 'vue';

// 定义响应式变量list,用于存储数据项
const list = ref(['html', 'css', 'js'])

// 定义handle方法,用于处理子组件发送的数据
const handle = (event) => {
  // 打印接收到的数据
  console.log(event);
  // 将接收到的数据添加到list数组中
  list.value.push(event)
}
</script>

<style lang="css" scoped></style>
  • tianjia :订阅该事件:<Child @tianjia="handle"></Child>。 也就是说每当子组件触发名为 tianjia 的事件时,父组件的 handle 方法将被执行。
  • handle:处理从子组件传来的数据,并将其推送到 list 数组中。

子组件:

js 复制代码
<template>
  <div class="input-group">
    <!-- 输入框,其值与value响应式变量双向绑定 -->
    <input type="text" v-model="value">
    <!-- 按钮,点击时触发add函数 -->
    <button @click="add">添加</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const value = ref('')

// 创建可触发的事件对象,声明'tianjia'事件
const emits = defineEmits(['tianjia'])

// 定义add函数,当按钮被点击时执行
const add = () => {
  // 触发'tianjia'事件,向父组件发送value的当前值
  emits('tianjia', value.value)
}

</script>

<style lang="css" scoped></style>
  • 子组件 :通过defineEmits声明可以触发的自定义事件tianjia,并通过emits('tianjia', value.value)将数据发送给父组件。
  • 父组件 :在模板中通过@tianjia="handle"监听子组件的tianjia事件,当事件触发时,执行handle方法,将接收到的数据打印并添加到list数组中,实现数据的更新和显示。

2. v-model数据绑定

父组件借助v-model将数据绑定给子组件,子组件创建'update:xxx'事件,并将接收到的数据修改后emits出来 父组件:

js 复制代码
<template>
<!-- 使用v-model绑定list数据到子组件的list prop -->
  <Child v-model:list="list"></Child>

  <!-- 显示列表 -->
  <div class="child">
    <ul>
      <!-- 遍历list数组,显示每一个元素 -->
     <li v-for="item in list">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
// 导入子组件Child
import Child from "@/components/child3.vue";
// 导入Vue的ref响应式引用
import { ref } from 'vue';

// 定义响应式变量list,用于存储数据项
const list = ref(['html', 'css', 'js'])

</script>

<style lang="css" scoped></style>

子组件:

js 复制代码
<template>
  <!-- 输入框和按钮 -->
 <div class="input-group">
   <!-- 输入框,其值与value响应式变量双向绑定 -->
    <input type="text" v-model="value">
    <!-- 按钮,点击时触发add函数 -->
    <button @click="add">添加</button>
  </div>
</template>

<script setup>
// 导入Vue的ref响应式引用
import { ref } from 'vue';

// 定义响应式变量,用于存储输入框的值
const value = ref('')

// 定义props,接收父组件传递的list数据
const props = defineProps({
  list: {
    type: Array,
    default: () => []
  }
})

// 创建可触发的事件对象,声明'update:list'事件
const emits = defineEmits(['update:list'])

// 定义add函数,当按钮被点击时执行
const add = () => {
  // 创建一个新数组,避免直接修改父组件传递的数组
  const arr = [...props.list]
  // 向数组中添加新值
  arr.push(value.value)
  // 触发'update:list'事件,向父组件发送更新后的数组
  emits('update:list', arr)
}

</script>

<!-- 样式部分 -->
<style lang="css" scoped></style>

关键点解析:

  • 父组件 :使用v-model:list="list"list数据绑定到子组件。v-model背后实际上是解构为v-bindv-on,分别绑定数据和监听特定的事件。

  • 子组件

    • 通过defineProps定义list属性,接收父组件的数据。
    • 使用defineEmits声明update:list事件,这是v-model机制识别的特殊事件。
    • add方法中,不直接修改props.list,而是创建新数组并修改,然后通过emits('update:list', arr)触发事件,将更新后的数组发送回父组件。

3. defineExpose()暴露数据

父组件通过ref获取子组件中defineExpose()暴露出来的数据

父组件:

js 复制代码
<template>
  <!-- 创建子组件引用 -->
  <Child ref="childRef"></Child>

  <!-- 显示子组件中暴露的list数据 -->
  <div class="child">
    <ul>
      <!-- 遍历childRef中的list数组,显示每一个元素 -->
      <li v-for="item in childRef?.list">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
// 导入子组件Child
import Child from "@/components/child4.vue";
// 导入Vue的ref响应式引用和onMounted生命周期钩子
import { ref, onMounted } from 'vue';

// 创建对子组件的引用
const childRef = ref(null)

// 使用onMounted确保子组件已经被挂载
onMounted(() => {
  // 输出子组件实例,检查是否可以访问
  console.log(childRef.value);
})

</script>

<style lang="css" scoped></style>

子组件:

js 复制代码
<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'])
// 定义add函数,当按钮被点击时执行
const add = () => {
  // 向list数组中添加新值
  list.value.push(value.value)
}

// 使用defineExpose明确指定哪些数据或方法对外公开
defineExpose({ list })

</script>

<style lang="css" scoped></style>

关键点解析:

  • 父组件 :通过ref创建对子组件的引用childRef,并在onMounted钩子中安全地访问子组件实例。在模板中使用childRef?.list来访问子组件中暴露的list数据。
  • 子组件 :使用defineExpose暴露list数据,使得父组件或其他外部组件能够直接访问和操作list
相关推荐
深情废杨杨23 分钟前
前端vue-插值表达式和v-html的区别
前端·javascript·vue.js
GHUIJS23 分钟前
【vue3】vue3.3新特性真香
前端·javascript·vue.js
众生回避29 分钟前
鸿蒙ms参考
前端·javascript·vue.js
洛千陨29 分钟前
Vue + element-ui实现动态表单项以及动态校验规则
前端·vue.js
GHUIJS1 小时前
【vue3】vue3.5
前端·javascript·vue.js
计算机学姐2 小时前
基于python+django+vue的家居全屋定制系统
开发语言·vue.js·后端·python·django·numpy·web3.py
Ripple1112 小时前
Vue源码速读 | 第二章:深入理解Vue虚拟DOM:从vnode创建到渲染
vue.js
秋沐2 小时前
vue中的slot插槽,彻底搞懂及使用
前端·javascript·vue.js
QGC二次开发3 小时前
Vue3 : Pinia的性质与作用
前端·javascript·vue.js·typescript·前端框架·vue
想退休的搬砖人4 小时前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js