likes
comments
collection
share

VUE入门:父子组件通讯

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

前言

在Vue.js的世界里,组件化开发是构建复杂应用的基石。其中,父子组件间的有效通讯是确保应用流畅运行的关键。实现父子通信对于提高开发效率、维护应用结构清晰至关重要。本文将深入探讨Vue中父子组件通讯的多种方式,助力你构建更加灵活、可扩展的Vue应用。

例子,预热

实现点击提交按钮和enter键添加代办事项

在vue项目中我们开发一个代办事项提交按钮是十分简单的

  1. 打造input框,为input框双向绑定用户输入的内容,还可以加上enter时间作为优化
  2. 准备好按钮,绑定点击事件,在js中打造函数
  3. 将数据全部变为响应式

在以下代码中,我为大家准备好了初始list放在类名为child的div中,在其中配置好了我们的list,实现当用户点击按钮时,list就会响应式被添加

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

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

    </ul>
  </div>
</template>

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

const list = ref(['绵绵冰1', '绵绵冰2', '绵绵冰3'])
const value = ref('')
function add() {
  list.value.push(value.value)
  value.value = ''
}

</script>

实现功能如下:

VUE入门:父子组件通讯

实现组件式开发

父子组件通讯

现在,我想将这块代码拆分,将类名为child的div放入另一个子组件中,我该怎么实现这两个组件的通讯?

父组件怎么把东西给子组件?在这个问题中我们需要将父组件App.vue中的list列表给到子组件,需要3步

  1. 添加 <Child />标签, 在其中绑定变量
 <Child :list="list"></Child>
  1. 在父组件中引入子组件:
import Child from '@/components/child.vue'  //  引入子组件
  1. 最后在子组件中通过 defineProps()接收父组件传递的数组
defineProps({
    list: {
        type: Array,
        default: () => []
    }
})

经此3步依旧可以实现添加事项功能。

子父组件通讯

在父子通讯中,因为在父组件中通过 import 引入了子组件,所以对于父组件需要传递的东西只需要绑定后由子组件通过 defineProps接收即可,但是子组件向父组件传递时会有不同,他需要使用许多方法。那么 当子组件需要将变量传递给父组件时有那些方法?针对以下的案例,我们来逐步探究

父组件:

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

    <Child></Child>

</template>

<script setup>
import Child from '@/components/child.vue'

import { ref } from 'vue'

const list = ref(['绵绵冰1', '绵绵冰2', '绵绵冰3'])

</script>

子组件:

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

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

const value = ref('')
const add = () => {
    list.value.push(value.value)
}
</script>

一, emit 发布订阅模式

我们可以使用大名鼎鼎的订阅模式,他的步骤很简单,我们只需要在子组件中创建,发布一个事件,然后在父组件中订阅这个世界,最后通过事件参数将新输入的值传到父组件中

1. 子组件创建事件

通过 defineEmits()创建一个事件,我为这个事件取名为1

const emit = defineEmits(['1'])

2. 子组件发布事件

在点击事件中发布这个事件,将点击事件自带的事件参数也携带出去,携带的事件参数为输入框中输入的值

const add = () => {
    emit('1', value.value)
}

3. 父组件订阅事件

在父组件中绑定子组件中发布的事件,被称作“订阅事件”。将订阅的参数重新绑定新函数执行操作

 <Child @1="handle"></Child>

4 导入事件参数

当点击事件触发时,子组件将发布订阅时携带的参数传递给父组件,父组件通过push方法将该事件参数放入列表中

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

二,通过v-model+: 绑定

这是更加优雅的方法,建立在父子通讯的基础上,在v-bind:绑定变量后再通过 v-model双向绑定,监听子组件中变量的变化,拿到变量。

1. 在父组件中通过 v-model+:绑定变量

    <Child v-model:list="list"></Child>

2. 通过 defineProps()接收父组件的数组,把值赋予props

const props = defineProps({
    list: {
        type: Array,
        default: () => { }
    }
})

3. 将子组件中变更的数值传入props

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

这就像是父组件将需要变更的变量list传给了子组件,子组件利用defineProps()接收后用props这个篮子装着,然后对这个篮子进行操作。因为这个变量被双向绑定,所以父组件能监听到这个篮子里发送的变化。

但是这种方式仍有弊端,当把原数据给到多个子组件时容易造成数据混乱。所以我们需要保证原数据不被修改,我们可以结合发布订阅的方式,将父组件传过来的原数组定义为一个新数组,对这个数组进行操作;创建一个事件,将这个经过更新后的新数组当作事件参数被发布出去;通过绑定的list,在v-model的对事件参数和list同步下会做出变更

优化后的代码如下:

onst emit = defineEmits(['updata:list'])
const add = () => {
    const arr = props.list;
    arr.push(value.value);

    emit('updata:list', arr)// 这段代码会促使v-model更改本地list
}

三,使用 ref访问子组件

当原数据和更改操作在子组件中时我们可以通过为父组件中的子组件标签添加ref访问子组件。考虑到异步问题,我们需要通过判断子组件是否加载完成,并且在子组件中通过 defineExpose({ list })抛出变量,最后在父组件中创造响应式数据。

父组件操作详解:

<template>
    // 在子组件标签中使用ref,目的是访问子组件;
    // 将ref返回一个变量目的是创建子组件的实例引用
    <Child ref="newList"></Child>

    <div class="child">
        <ul>
    // 因为当子组件加载完成后父组件才能拿到数据,newList才有值
    // 所以使用操作符`?`来判断,当newList有值(子组件加载完成时)执行步骤
            <li v-for="item in newList?.list">{{ item }}</li>
        </ul>
    </div>

</template>

<script setup>
import Child from '@/components/child.vue'

import { ref } from 'vue'
// 创建一个响应式的数据引用,子组件的数据将会被响应式的赋值给newList
const newList = ref(null)

</script>

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

子组件操作详解:

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

</template>

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

const list = ref(['绵绵冰1', '绵绵冰2', '绵绵冰3'])

const add = () => {
    list.value.push(value.value)
}
// 向外暴露更改后的变量,方便父组件接收
defineExpose({ list })
</script>

小结

本章内容中我们学习了父子通讯的四种方法:

  1. 父子组件通讯 --- 父组件将值v-bind绑定传给子组件,子组件使用defineProps接收这个值
  2. 子父组件通讯 --- 借助发布订阅机制,子组件负责发布事件并携带参数,父组件订阅该事件通过事件参数获得子组件中的值
  3. 子父组件通讯 --- 父组件借助v-model将数据绑定给子组件,子组件创建'update:xxx'事件,并接收到的数据修改后emits出来
  4. 子父组件通讯 --- 父组件通过ref获取组件中defineExpose() 暴露出来的数据
转载自:https://juejin.cn/post/7392412742097502260
评论
请登录