likes
comments
collection
share

揭秘Vue中组件通信 —— v-bind、emits、v-model、ref组件通信就是指组件之间的数据传递。由于组件的

作者站长头像
站长
· 阅读数 21

前言:

组件通信就是指组件之间的数据传递。由于组件的数据是独立的,无法直接访问其他组件的数据,所以想要使用其他组件数据必须通过组件通信

如图,组件①与组件②之间的通信: 揭秘Vue中组件通信 —— v-bind、emits、v-model、ref组件通信就是指组件之间的数据传递。由于组件的

可以看到组件①②各司其职,通过组件通信成功进行交互,完成了添加新值的效果。

那么接下来我们要讲的,正通过这两个组件来讲解一种父子通信三种子父通信共四种方法基础的通信方法,与实际应用它们需要注意的点。

初始状态:

先来看,这是一段没有将分这两个组件分开,写在一起时的初始代码,然后我们再来介绍将它们分开需要用到的组件通信方式:

<template>
  // **********组件1**********
  <div class="input-group">
    <input type="text" v-model="value">
    <button @click="add">添加</button>
  </div>
  
 // **********子组件2**********
   <div class="child">
    <ul>
      <li v-for="item in list">{{item}}</li>
    </ul>
   </div>

</template>

<script setup>
import { ref } from 'vue'

const list = ref(['html', 'css', 'js'])
const value = ref('')

const add = () => {
  list.value.push(value.value)
  value.value = ''
}

</script>

<style lang="css" scoped>

</style>

组件通信方式:

父子组件通信:v-bind

现在我们把输入作为父组件,展示作为子组件来实现父子组件通信。

揭秘Vue中组件通信 —— v-bind、emits、v-model、ref组件通信就是指组件之间的数据传递。由于组件的

原理: 父组件将值通过v-bind绑定传给子组件,子组件使用definePrope接收。

在代码第八行的<child :msg="toChild"></child>,我们通过使用v-bind使msg(自命名)绑定toChild(点击添加按钮添加进来的值)值,传给子组件。

****************************************父组件****************************************
<template>

  <div class="input-group">
    <input type="text" v-model="value">
    <button @click="add">添加</button>
  </div>

  <child :msg="toChild"></child>             // 将值通过`v-bind`绑定传给子组件

</template>

<script setup>
import { ref } from 'vue'
import Child from '@/components/child.vue'   // 以Child命名,导入子组件child.vue

const list = ref(['html', 'css', 'js'])
const value = ref('')
const toChild = ref('')

const add = () => {
  toChild.value = value.value
}

</script>

<style lang="css" scoped>

</style>

顺带讲解一下子组件收到父组件的值,然后将其传入子组件数组的方法。这里先是第14行的defineProps接收到父组件传的值,再通过watch监听到msg的值产生变更,于是push(newVal),将变更后的值推入数组。便达到了组件通信效果!

****************************************子组件****************************************
<template>
   <div class="child">
    <ul>
      <li v-for="item in list">{{item}}</li>
    </ul>
   </div>
</template>

<script setup>
import { defineProps, ref, watch } from 'vue'

const list = ref(['html', 'css', 'js'])
const props = defineProps({            // 子组件使用`definePrope`接受
    msg: ''
})

watch(                                // 监听defineProps穿进来的值是否发生变更,如是则推入数组
    () => props.msg,
    (newVal, oldVal)=>{
        list.value.push(newVal)
    }
)

</script>

<style lang="css" scoped>

</style>

子父组件通信①:emits

现在我们把输入作为子组件,展示作为父组件来实现三种子父组件通信。

揭秘Vue中组件通信 —— v-bind、emits、v-model、ref组件通信就是指组件之间的数据传递。由于组件的

以下是第一种子父通信实现,其原理是借助发布订阅机制,子组件负责发布事件并携带参数,父组件订阅事件通过事件参数获取子组件提供的值。这就是大名鼎鼎的发布订阅机制!

在第13行,我们通过const emits = defineEmits(['add1'])创建了一个名为 add1 的事件,再通过emits('add1', value.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 emits = defineEmits(['add1'])  // 创建一个add事件 
const add = () => {
//   将value给到父组件
   emits('add1', value.value)       // 发布事件
}
</script>

<style lang="css" scoped>

</style>

既然子组件发布了 add1 这个事件,父组件就在第4行通过@add1="handle"命名一个 handle 来绑定这个事件,也被称为订阅add1事件

正常流程下子组件接收到值,通过 add1 事件发布给父组件,于是父组件中 handle 函数调用,运行list.value.push(event)将传过来的事件参数推入数组。便达到了组件通信效果!

<template>
****************************************父组件****************************************
<!-- 订阅add1事件 -->
 <child @add1="handle"></child>

   <div class="child">
    <ul>
      <li v-for="item in list">{{item}}</li>
    </ul>
   </div>

</template>

<script setup>
import Child from '@/components/child2.vue'   // 以Child命名,导入子组件child.vue
import { ref } from 'vue'
const list = ref(['html', 'css', 'js'])

const handle = (event) => {
  list.value.push(event)
}

</script>

<style lang="css" scoped>

</style>

如果把父组件的数组(const list = ref(['html', 'css', 'js']))比作篮子子组件中添加进来的新值比作苹果,这种方法就像子组件把苹果传递给父组件,父组件再把它放进篮子里。

子父组件通信②:v-model

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

同样是实现子父通信,与①方法不同的是,这种方法做的是子组件把父组件的篮子拿过来,把苹果放进去,然后再把篮子还给父组件。

在父组件中,我们先通过v-model:list="list"绑定 list 属性,并传递给父组件,父组件中同样用defineProps接收。到这里算是初步完成了。

****************************************父组件****************************************
<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 Child from '@/components/child3.vue'
import { ref } from 'vue'
const list = ref(['html', 'css', 'js'])



</script>

<style lang="css" scoped>

</style>

为什么说是初步完成呢?因为实际应用中不建议直接操作父组件传过来的数据,因为这个数组属于父组件,如果子组件能修改父组件中的值,那多少是有些不合适的,面对一父多子的情况下也势必造成混乱。

因此我们使用const emits = defineEmits(['update:list'])绑定数组 list 的更新事件,当24行的 push 将新值推入数组后,emits('update:list', arr)又将这个事件抛出,这样 modle 就会接收到这个事件,做到实际上的值为父组件修改的效果!

****************************************子组件****************************************
<template>
  <div class="input-group">
    <input type="text" v-model="value">
    <button @click="add">添加</button>
  </div>
</template>

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

const props = defineProps({                  // 接收传过来的值
    list: {
        type: Array,                         // 当子组件中传过来的值属性不为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>

子父组件通信③:ref

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

与上两种子父组件通信不同的是,现在我们要介绍的是一种数组(const list = ref(['html', 'css', 'js']))属于子组件下的通信(这样的好处是子组件可以直接把值添加进数组中)。

那么第一步我们要做的就是让父组件能读到子组件数组中的值,于是我们通过第4行与第19的ref="childRef"const childRef = ref(null)创建childRef变量并打到ref身上,这样一来我们就能通过ref访问到子组件通过defineExpose提供出来的数据。

需要注意的是这里父组件中的读取涉及到子父组件的加载顺序,简单来说就是父组件读取时子组件还没加载完的情况。于是我们使用第8行的原生方法childRef?.list意思是childRef中有值时再将 list 循环,实在是异常的方便。

****************************************父组件****************************************
<template>
<!-- 订阅add1事件 -->
 <child ref="childRef"></child>

   <div class="child">
    <ul>
      <li v-for="item in childRef?.list">{{item}}</li>
    </ul>
   </div>

</template>

<script setup>
import Child from '@/components/child4.vue'
import { onMounted, ref } from 'vue'


const childRef = ref(null)         //  子组件动结构

</script>

<style lang="css" scoped>

</style>

在子组件通过defineExpose将 list 暴露给父组件。

****************************************子组件****************************************
<template>
  <div class="input-group">
    <input type="text" v-model="value">
    <button @click="add">添加</button>
  </div>
</template>

<script setup>
import { ref, defineProps } from 'vue'
const value = ref('')
const list = ref(['html', 'css', 'js'])

const add = () => {
  list.value.push(value.value)
}

defineExpose(  {list} ) // 暴露出list给父组件

</script>

<style lang="css" scoped>

</style>

至此,大功告成!

最后

总结一下就是这里提供了一种父子组件通信,三种字符组件通信,它们的原理分别是

  1. 父子组件通讯 --- 父组件将值 v-bind 绑定传给子组件,子组件使用 definePrope 接受。

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

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

  4. 子父组件通讯③ --- 父组件通过 ref 获取子组件中 defineExpose() 暴露出来的数据。

以及实际应用上需要注意的各方面细节,感谢收看!

转载自:https://juejin.cn/post/7400671870872076322
评论
请登录