Vue通信艺术:深入理解父子组件间消息传递技巧

前言

在vue搭建项目的时候,会有父组件和子组件,你有没有思考一下当我想要得到父组件或者子组件的数据时,该怎么实现父组件和子组件之间的通讯呢?如果知道的话,那太nice啦,不太清楚的话那就往下看吧~

正文

我们通过实现下面的小demo来介绍父组件和子组件之间的通讯吧~

父组件传子组件

两种方式:

父组件中存放列表数据和input,子组件来实现渲染列表,传列表给子组件

  • 父组件通过:list="list"list传给子组件
ruby 复制代码
 <Child :list="list"></Child>
  • 子组件通过defineProps接收父组件传入的list
php 复制代码
defineProps({
  list: {
      type: Array,
      default: () => [] // 默认值
  }
});

App.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 Child from '@/components/child.vue';
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>

child.vue

xml 复制代码
<template>
  <div class="child">
  <ul>
    <li v-for="(item, index) in list" :key="index">{{ item }}</li>
  </ul>
</div>
</template>

<script setup>
import { defineProps } from 'vue';

defineProps({
  list: {
      type: Array,
      default: () => [] // 默认值
  }
});
</script>

<style lang="css" scoped>

</style>

父组件存放input,子组件放列表并实现渲染,父组件传input内的值给子组件

  • 父组件通过定义一个变量toChild,将value赋值给toChild
  • 通过:msg="toChild"toChildmsg传递给子组件
ruby 复制代码
  <Child :msg="toChild"></Child>
  • 子组件通过 defineProps接收msg
ini 复制代码
const props = defineProps({
  msg:''
});
  • 通过watch来监听msg,对列表数据进行更新
scss 复制代码
watch(
  () => props.msg,
  (newValue, oldValue) => {
    list.value.push(newValue);
  }
);

App.vue

ini 复制代码
<template>
  <div class="input-group">
    <input type="text" v-model="value">
    <button @click="add">添加</button>
  </div>

  <Child :msg="toChild"></Child>
</template>
<script setup>
import Child from '@/components/child.vue';
import { ref } from '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>

child.vue

xml 复制代码
<template>
  <div class="child">
  <ul>
    <li v-for="(item, index) in list" :key="index">{{ item }}</li>
  </ul>
</div>
</template>

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

const list = ref(['html', 'css', 'js']);
const props = defineProps({
  msg:''
});

watch(
  () => props.msg,
  (newValue, oldValue) => {
    list.value.push(newValue);
  }
);
</script>

<style lang="css" scoped>

</style>

子组件传父组件

子组件中存放input,父组件中存放数据清单并渲染数据

第一种方法

  • 子组件创建一个add1事件
ini 复制代码
const emits = defineEmits(['add1']); // 创建一个add事件
  • 子组件在add函数中发布事件,并传入数据value
csharp 复制代码
const add = () => {
  // 将value给到父组件
emits('add1', value.value);// 发布事件
}
  • 在父组件中订阅事件add1
ini 复制代码
 <Child @add1="handle"></Child>
  • 当触发add1事件时,也就是点击增加时触发handle函数
csharp 复制代码
const handle = (event) => {
    list.value.push(event);
  }
App.vue
xml 复制代码
<template>
    <!-- 订阅add1事件 -->
    <Child @add1="handle"></Child>

    <div class="child">
        <ul>
            <li v-for="(item, index) in list" :key="index">{{ item }}</li>
        </ul>
    </div>
  </template>
  <script setup>
  import Child from '@/components/child2.vue';
  import { ref } from 'vue';
  
  const list = ref(['html', 'css', 'js']);

  const handle = (event) => {
    list.value.push(event);
  }
 
  </script>
  
  <style lang="css" scoped>
  
  </style>
child.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';

const value = ref('');
const emits = defineEmits(['add1']); // 创建一个add事件

const add = () => {
  // 将value给到父组件
emits('add1', value.value);// 发布事件
}
</script>

<style lang="css" scoped>

</style>

第二种方法

  • 父组件通过 v-model:list="list"将list传给子组件
ini 复制代码
 <Child v-model:list="list"></Child>
  • 子组件通过props接收list
ini 复制代码
const props = defineProps({
  list:{
    type: Array,
    default: () => []
  }
});
  • 子组件可以直接在add函数通过props.list.push(value.value);来实现对列表数据的更新,但是不太建议这种方法,不太建议直接操作父组件给过来的数据,这样的话代码的可维护性比较差
  • 子组件通过使用defineEmits来新建一个emits函数,该函数用于告知父组件,子组件希望更新一个名为 list 的数据,再新建一个变量arr来接收list,来更新arr,再通过emits('update:list', arr);来实现对list的更新
ini 复制代码
const emits = defineEmits(['update:list']);

const add = () => {
const arr = props.list;
arr.push(value.value);
emits('update:list', arr);
}
App.vue
xml 复制代码
<template>
    <Child v-model:list="list"></Child>

    <div class="child">
        <ul>
            <li v-for="(item, index) in list" :key="index">{{ 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>
child.vue
ini 复制代码
<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 props = defineProps({
  list:{
    type: 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>

第三种方法

将列表数据存放在子组件中:

  • 子组件通过defineExpose({list});list暴露出来
  • 父组件通过ref="childRef"赋予了一个名字为childRef的引用
ini 复制代码
 <Child ref="childRef"></Child>
  • 父组件通过 const childRef = ref(null);创建一个响应式的引用(ref)对象childRef
  • 通过(item, index) in childRef?.list来遍历列表数据
xml 复制代码
 <div class="child">
        <ul>
            <li v-for="(item, index) in childRef?.list" :key="index">{{ item }}</li>
        </ul>
    </div>

-childRef?.list 这是一种安全访问属性的方式,避免了尝试访问undefinednull对象的属性时抛出错误,也就是只有当childRef不为nullundefined时,才会尝试访问其list属性,因为此时子组件可能尚未被创建或挂载,直接访问childRef.list会导致错误,因为nullundefined没有list属性

App.vue
xml 复制代码
<template>
    <Child ref="childRef"></Child>

    <div class="child">
        <ul>
            <li v-for="(item, index) in childRef?.list" :key="index">{{ item }}</li>
        </ul>
    </div>
  </template>
  <script setup>
  import Child from '@/components/child4.vue';
  import { ref } from 'vue';
  
  const childRef = ref(null);
</script>
  
  <style lang="css" scoped>
  
  </style>
child.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';

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

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

defineExpose({list});// ==> defineExpose({list: list}); 心甘情愿暴露出list
</script>

<style lang="css" scoped>

</style>

结语

  1. 父子组件通讯 --- 父组件用v-bind绑定传给子组件,子组件用defineProps接收数据
  2. 子父组件通讯 --- 子组件用defineEmits创建一个事件,在发布该事件时传入数据,父组件通过v-on来订阅该事件
  3. 子父组件通讯 --- 父组件借助v-model将数据绑定给子组件,子组件创建'update:xxx'事件,并将接收到的数据修改后emits出来
  4. 子父组件通讯 --- 父组件通过ref获取子组件中的defineExpose()暴露出来的数据
相关推荐
科技探秘人11 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人11 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR17 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香18 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969321 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai27 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_91535 分钟前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫6 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试