前言
在Vue中,组件通讯主要通过props和events来实现。通过props,父组件可以向子组件传递数据,子组件则通过props接收并使用这些数据。而通过events,子组件可以向父组件发送消息或触发事件,父组件可以监听这些事件并做出响应。
此外,Vue还提供了provide和inject API,允许祖先组件向所有后代组件注入依赖项,而无需显式传递props。这在跨多层次嵌套的组件中特别有用。
今天我们就来聊一聊vue中的组件通讯。
1. 话题引入
我们首先来看一下最原始的一段vue
xml
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">提交</button>
</div>
<div>
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue'
const list = ref(['html', 'css', 'js'])
let value = ref('')
const add = () => {
list.value.push(value.value)
value.value = ''
}
</script>
<style lang="css" scoped>
</style>
这里面的作用很简单,我们通过点击input框可以让列表增加内容。
2.父子组件通讯(父将数据给子)
父子组件通讯 --- 父组件将值v-bind绑定传给子组件,子组件使用defineProps接受
我们文件的大致格式如上,我们这一次的父组件是App1.vue,子组件就是对应的child1.vue.
App1.vue
xml
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">提交</button>
</div>
<child :list="list"></child>
</template>
<script setup>
import { ref } from 'vue'
import child from '@/components/child1.vue'
const list = ref(['html', 'css', 'js'])
let value = ref('')
const add = () => {
list.value.push(value.value)
value.value = ''
}
</script>
<style lang="css" scoped>
</style>
-
<child>组件:- 将父组件的
list数组作为属性传递给子组件<child>。
- 将父组件的
-
<script setup>:- 使用
ref函数创建了两个响应式变量:list和value。 list是一个数组,初始包含三个字符串元素:'html', 'css', 'js'。value是一个字符串,初始为空。- 定义了
add函数,用于向list数组添加新的项,并清空value。
- 使用
child1.vue
xml
<template>
<div class="child">
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
defineProps({
list: {
type: Array,
default: () => []
}
})
</script>
<style lang="css" scoped>
</style>
-
模板部分 (
<template>) :- 使用了 Vue 的指令
v-for,遍历list数组中的每个item,并将每个item显示为一个<li>列表项。
- 使用了 Vue 的指令
-
<script setup>部分:- 使用了
defineProps函数,声明了一个名为list的 prop。 list的类型被指定为数组 (type: Array),并设置了默认值为空数组 (default: () => [])。- 这样做是为了确保
<child>组件能够正常接收和处理来自父组件的list数组数据。
- 使用了
这段代码的大致思路是,首先我们将父组件的list数组传给了子组件,然后子组件以defineProps的形式进行接收,然后进行我们的渲染工作。
App2.vue
xml
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">提交</button>
</div>
<child :msg="tochild"></child>
</template>
<script setup>
import { ref } from 'vue'
import child from '@/components/child2.vue'
let value = ref('')
let tochild = ref('')
const add = () => {
tochild.value = value.value
}
</script>
<style lang="css" scoped>
</style>
这个 Vue 组件的结构设计了一个简单的输入表单和一个子组件的嵌套。
在<template>中:
- 有一个包含输入框和提交按钮的
.input-group容器。输入框通过v-model="value"实现双向数据绑定,将用户输入的内容同步到value变量。 - 点击按钮时,触发了
add方法,这个方法将value变量的值赋给tochild变量。
接着,通过 <child :msg="tochild"></child> 将 tochild 变量的值作为 msg 属性传递给了名为 child 的子组件。
在 <script setup> 部分:
- 使用了 Vue 3 的 Composition API 中的
ref函数来声明了两个响应式变量value和tochild,它们分别用于存储输入框的内容和传递给子组件的数据。 add函数定义了按钮点击时的行为,将当前输入框中的值赋给tochild变量,以便将其传递给子组件。
child2.vue
xml
<template>
<div class="child">
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { watch, ref } from 'vue'
const list = ref(['html', 'css', 'js'])
const prop = defineProps({
msg:''
})
watch(() => prop.msg, (newVal, OldVal) => {
list.value.push(newVal)
}
)
</script>
<style lang="css" scoped>
</style>
这个 Vue 组件的目的是展示一个简单的列表,列表的内容来自于 list 变量,并且可以接收一个名为 msg 的 prop 属性,将其值添加到列表中。在<template> 中: <ul> 标签用于显示一个无序列表,其中的每个 <li> 标签通过 v-for="item in list" 遍历 list 变量中的每个元素,并将其显示在列表中。
在 <script setup> 部分:
- 使用了 Vue 3 的 Composition API 中的
ref和watch函数。 list是一个包含初始值为['html', 'css', 'js']的响应式数组,用于存储列表中的项目。- 使用
defineProps定义了一个名为msg的 prop 属性,允许父组件向当前组件传递数据。 watch函数监听了prop.msg的变化,当msg属性的值发生变化时,会执行回调函数将新值newVal添加到list数组中。 这里我们直接让数组归子组件所有,我们直接让父组件传入更新后的值给子组件。
3.子父组件通讯(子将数据给父)
1.子父组件通讯 --- 借助发布订阅机制,子组件负责发布事件并携带参数,父组件订阅该事件通过事件参数获取子组价提供的值
App3.vue
xml
<template>
<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/child3.vue'
const list = ref(['html', 'css', 'js'])
const handle = (e) => {
list.value.push(e)
}
</script>
<style lang="css" scoped>
</style>
-
使用了
child组件,并监听了子组件触发的add1自定义事件,当事件触发时调用handle方法。 -
显示一个包含动态生成内容的无序列表 (
<ul>),使用v-for="item in list"循环遍历list变量中的每个元素,并在每个<li>中显示该元素的内容。 -
导入了 Vue 3 的
ref函数,用于声明响应式变量。 -
定义了
list变量作为一个响应式数组,初始包含['html', 'css', 'js']三个元素,用于存储列表显示的内容。 -
定义了一个
handle函数,该函数接收一个参数e,将其作为新的元素添加到list数组中。
child3.vue
xml
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">提交</button>
</div>
</template>
<script setup>
import {ref} from 'vue'
let value = ref('')
const emits = defineEmits(['add1'])
const add = () => {
emits('add1', value.value)
}
</script>
<style lang="css" scoped>
</style>
在模板部分 (<template>) 中:
- 包含一个输入框 (
<input>) 和一个按钮 (<button>)。 - 使用了
v-model="value"来实现双向数据绑定,将输入框中的内容与value变量进行绑定,即用户在输入框中输入的内容会同步更新到value变量中。 - 点击按钮时触发
add方法。
在 <script setup> 部分:
- 导入了 Vue 3 的
ref函数,用于声明一个响应式变量value,其初始值为空字符串'',用于存储输入框中的内容。 - 使用
defineEmits(['add1'])定义了一个名为add1的自定义事件,用于向父组件发送数据。 - 定义了
add函数,当按钮被点击时,通过emits('add1', value.value)发送add1事件,并将当前value变量的值作为参数传递给父组件。
2.子父组件通讯 --- 父组件借助v-model将数据绑定给子组件,子组件创建'update:xxx'事件,并接收到的数据修改后emits出来
App4.vue
xml
<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/child4.vue'
let value = ref('')
const list = ref(['html', 'css', 'js'])
</script>
<style lang="css" scoped>
</style>
-
子组件的使用:
- 在模板部分 (
<template>) 中,使用了<child v-model:list="list"></child>,这表示在父组件中将list变量作为child组件的v-model绑定。 v-model:list="list"会将父组件的list变量作为一个 prop 传递给child组件,并且监听child组件触发的更新事件来同步数据。
- 在模板部分 (
-
列表的渲染:
- 父组件中定义了一个
<div class="child">,其中包含一个无序列表 (<ul>)。
- 父组件中定义了一个
child4.vue
xml
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">提交</button>
</div>
</template>
<script setup>
import {ref} from 'vue'
let value = ref('')
const prop = defineProps({
list: {
type: Array,
default: () => []
}
})
const emits = defineEmits(['update:lis'])
const add = () => {
// prop.list.push(value.value)
const arr = prop.list
arr.push(value.value)
emits('update:lis',arr)
}
</script>
<style lang="css" scoped>
</style>
-
输入框和按钮:
- 在模板部分 (
<template>) 中,有一个包含输入框和按钮的<div>,类名为input-group。 - 输入框 (
<input>) 使用了v-model="value"来实现双向数据绑定,即输入框中的内容会与value变量同步。 - 提交按钮 (
<button @click="add">提交</button>) 通过点击触发add方法。
- 在模板部分 (
-
脚本部分 (
<script setup>) 解释:- 使用
ref函数引入value变量,初始值为空字符串'',用于存储输入框中的内容。 - 使用
defineProps定义了一个名为list的 prop,类型为数组 (Array),默认值为空数组 (() => [])。 - 使用
defineEmits定义了一个名为update:lis的自定义事件,用于向父组件传递更新后的列表数据。
- 使用
3.子父组件通讯 --- 父组件通过ref获取子组件中defineExpose() 暴露出来的数据
App5.vue
xml
<template>
<child ref="reff"></child>
<div class="child">
<ul>
<li v-for="item in reff?.list">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import child from '@/components/child5.vue'
let value = ref('')
let reff = ref(null)
</script>
<style lang="css" scoped>
</style>
-
子组件引用:
- 在
<template>部分,通过<child ref="reff"></child>的方式引入了名为child的子组件,并通过ref="reff"将子组件实例存储在reff变量中,便于后续访问子组件的属性和方法。
- 在
-
列表展示:
- 在
<template>中的<ul>中,使用v-for="item in reff?.list"遍历reff引用的子组件中的list属性。这里通过?.安全访问操作符,确保在reff尚未被初始化之前不会抛出错误。 - 每个
<li>标签显示了item变量的内容,即子组件中列表中的每个项。
- 在
-
脚本部分 (
<script setup>) 解释:- 使用
ref函数声明了value和reff变量。value变量用于存储输入框的内容(在示例中未使用到),reff变量用于引用子组件的实例。 - 使用
import child from '@/components/child5.vue'导入了名为child的子组件,在当前组件中可以直接使用它。
- 使用
child5.vue
xml
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">提交</button>
</div>
</template>
<script setup>
import {ref} from 'vue'
let value = ref('')
const list = ref(['html', 'css', 'js'])
const add = () => {
list.value.push(value.value)
}
defineExpose({list:list})
</script>
<style lang="css" scoped>
</style>
输入框和按钮位于 <template> 部分。输入框通过 v-model="value" 双向绑定到 value 变量,允许用户输入文本并自动更新 value 的值。按钮通过 @click="add" 绑定了 add 方法,点击按钮时触发 add 方法。
在 <script setup> 部分,使用 import {ref} from 'vue' 导入 Vue Composition API 的 ref 函数。然后声明了两个响应式变量:
value:用于存储输入框中的文本内容,初始值为空字符串。list:用ref(['html', 'css', 'js'])初始化为一个包含三个字符串('html', 'css', 'js')的数组。
add 方法定义了一个箭头函数,当按钮被点击时会调用这个函数。它将 value.value 的值(即当前输入框中的文本)添加到 list.value 数组末尾。