• 你知道Vue中的组件通信原理吗?
  • 发布于 2个月前
  • 262 热度
    0 评论
前言
在Vue中,组件通讯主要通过props和events来实现。通过props,父组件可以向子组件传递数据,子组件则通过props接收并使用这些数据。而通过events,子组件可以向父组件发送消息或触发事件,父组件可以监听这些事件并做出响应。此外,Vue还提供了provide和inject API,允许祖先组件向所有后代组件注入依赖项,而无需显式传递props。这在跨多层次嵌套的组件中特别有用。
今天我们就来聊一聊vue中的组件通讯。
一. 话题引入
我们首先来看一下最原始的一段vue
<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'
// 堆代码 duidaima.com
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框可以让列表增加内容。

二.父子组件通讯(父将数据给子)
父子组件通讯 --- 父组件将值v-bind绑定传给子组件,子组件使用defineProps接受
我们文件的大致格式如上,我们这一次的父组件是App1.vue,子组件就是对应的child1.vue.
App1.vue
<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>
1.<child> 组件:
将父组件的 list 数组作为属性传递给子组件 <child>。
2.<script setup> :
.使用 ref 函数创建了两个响应式变量:list 和 value。
.list 是一个数组,初始包含三个字符串元素:'html', 'css', 'js'。
.value 是一个字符串,初始为空。
.定义了 add 函数,用于向 list 数组添加新的项,并清空 value。

child1.vue
<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>
1.模板部分 (<template>) :
使用了 Vue 的指令 v-for,遍历 list 数组中的每个 item,并将每个 item 显示为一个 <li> 列表项。

2.<script setup> 部分:
.使用了 defineProps 函数,声明了一个名为 list 的 prop。
.list 的类型被指定为数组 (type: Array),并设置了默认值为空数组 (default: () => [])。
.这样做是为了确保 <child> 组件能够正常接收和处理来自父组件的 list 数组数据。

这段代码的大致思路是,首先我们将父组件的list数组传给了子组件,然后子组件以defineProps的形式进行接收,然后进行我们的渲染工作。
App2.vue
<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
<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 数组中。
这里我们直接让数组归子组件所有,我们直接让父组件传入更新后的值给子组件。

三.子父组件通讯(子将数据给父)
1.子父组件通讯 --- 借助发布订阅机制,子组件负责发布事件并携带参数,父组件订阅该事件通过事件参数获取子组价提供的值
App3.vue
<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
<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
<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
<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
<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
<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 数组末尾。
用户评论