【前端学习指南】第三站 Vue 组件之间通信

🍭 Hello,我是爱吃糖的范同学

🔴 想把自己学习技术的经历和一些总结分享给大家!

🔴 通过这样的方式记录自己成长,同时沉淀自己的技术,我会把所有额外的时间和经历投放到CSDN和公众号(💥公号名:AIRC Team💥,欢迎关注,为大家准备了很多有关编程学习资料)文章的撰写。

🔴 希望能通过这样的方式让大家认识我,和我一起学习编程,共同进步!😃

希望能和大家一起进步和成长!坚持热爱!🎉🎉🎉


目录

[📌 Vue 组件 通信 概述:](#📌 Vue 组件 通信 概述:)

[📌 Vue 组件 父子 通信:](#📌 Vue 组件 父子 通信:)

[1.父组件 数据传递到 子组件:](#1.父组件 数据传递到 子组件:)

[2.子组件 数据传递到 父组件:](#2.子组件 数据传递到 父组件:)

[📌 Vue 组件 通信 Props:](#📌 Vue 组件 通信 Props:)

[1.Props 属性定义:](#1.Props 属性定义:)

[2.Props 属性校验:](#2.Props 属性校验:)

[2.1 类型校验:](#2.1 类型校验:)

[2.2 属性复杂校验:](#2.2 属性复杂校验:)

[3.Props 和 Data 对比,单向数据流:](#3.Props 和 Data 对比,单向数据流:)

[📌 Vue 组件 通信 v-model:](#📌 Vue 组件 通信 v-model:)

[📌 Vue 组件 通信 $attrs:](#📌 Vue 组件 通信 $attrs:)

[📌 Vue 组件 通信 refs、parent:](#📌 Vue 组件 通信 refs、parent:)

[📌 Vue 组件 通信 provide、inject:](#📌 Vue 组件 通信 provide、inject:)

[📌 Vue3 组件通信方式:](#📌 Vue3 组件通信方式:)

[📌 往期文章](#📌 往期文章)

[📌 今日一言](#📌 今日一言)


📌 Vue 组件 通信 概述:

组件通信是指组件与组件之间的数据传递。组件之间的数据是独立的,无法直接访问其他组件的数据。如果想使用其他组件的数据,就需要使用组件之间的通信方式,来把当前组件的数据传递给其他组件。

首先需要明确的是组件之间的关系:

首先明确组件之间的关系大致分为两类:父子关系 和 没有关系(世界上只有两种人:一种是人是义父,一种人不是义父 😏😏😏)

父子关系是指是组件之间是否存在包含关系,即一个组件中引入其他组件,当前组件是父组件,其他引入的组件是子组件。

针对这两种不同类型的关系,Vue 提出了不同的通信解决方案。


📌 Vue 组件 父子 通信:

  • 父组件通过 props 将数据传递给子组件
  • 子组件利用 $emit 通知父组件修改更新

1.父组件 数据传递到 子组件:

父组件通过在子组件使用标签中添加属性的方式将数据传递给子组件。子组件的 props 属性接收父组件传递过来的数据。

关于 Props 属性传递后面会单独进行解析,他除了可以实现从父组件向子组件传递数据外,还可以通过传递方法把数据从子组件传递给父组件。

2.子组件 数据传递到 父组件:

【自定义事件】自定义事件常用于:子 => 父

区分原生事件和自定义事件:

  • 原生事件:
    • 事件名是特定的(clickmosueenter等等)
    • 事件对象$event: 是包含事件相关信息的对象(pageXpageYtargetkeyCode
  • 自定义事件:
    • 事件名是任意名称
    • 事件对象$event: 是调用emit时所提供的数据,可以是任意类型!!!
    • 命名方式尽量不要驼峰式,而是采取keybab-case式,即 send-toy

首先,需要在父组件中提供一个接收子组件通知的监听函数。然后在子组件的属性中注册这个监听函数。子组件通过 $emit 触发通知事件,父组件监听到子组件的通知事件后,获取到子组件传递的数据,进行后续处理。

html 复制代码
<!--在父组件中,给子组件绑定自定义事件:-->
<Child @send-toy="toy = $event"/>

<!--注意区分原生事件与自定义事件中的$event-->
<button @click="toy = $event">测试</button>
html 复制代码
//子组件中,触发事件:
this.$emit('send-toy', 具体数据)

📌 Vue 组件 通信 Props:

1.Props 属性定义:

概述:props是使用频率最高的一种通信方式,常用与 :父 ↔ 子

  • 父传子 :属性值是非函数
  • 子传父 :属性值是函数
html 复制代码
<template>
  <div class="father">
    <h3>父组件,</h3>
		<h4>我的车:{{ car }}</h4>
		<h4>儿子给的玩具:{{ toy }}</h4>
		<Child :car="car" :getToy="getToy"/>
  </div>
</template>

<script setup lang="ts" name="Father">
	import Child from './Child.vue'
	import { ref } from "vue";
	// 数据
	const car = ref('奔驰')
	const toy = ref()
	// 方法
	function getToy(value:string){
		toy.value = value
	}
</script>
html 复制代码
<template>
  <div class="child">
    <h3>子组件</h3>
		<h4>我的玩具:{{ toy }}</h4>
		<h4>父给我的车:{{ car }}</h4>
		<button @click="getToy(toy)">玩具给父亲</button>
  </div>
</template>

<script setup lang="ts" name="Child">
	import { ref } from "vue";
	const toy = ref('奥特曼')
	
	defineProps(['car','getToy'])
</script>

Prop 就是组件上注册的一些自定义属性。Props 的作用是组件间传递数据。组件之间可以传递任意数量、任意类型的 Prop(属性)。

2.Props 属性校验:

为了避免 Props 属性在组件之间胡乱传递,Vue 提供了针对 Props 的校验规则。为指定组件的 Props 进行验证,不符合要求的 Prop 值会在控制台中输出错误提示,帮助开发者发现错误。

Vue 提供了四种校验语法:类型校验、非空校验、默认值、自动与校验。

2.1 类型校验:

将接收 Props 的写法改为接收对象的方式:

javascript 复制代码
props: {
    校验属性名: 数据类型
}

2.2 属性复杂校验:

除了上面基本的属性数据类型校验以外,Vue 提供了复杂的校验方式,包括了上述的四种校验语法。

javascript 复制代码
props: {
    待校验的属性名: {
        type: 数据类型,
        require: true, // 是否必填值
        default: 默认值,
        validator (value) {
            // 自定义校验逻辑
            return 是否通过校验
        }
    }
}

3.Props 和 Data 对比,单向数据流:

Props 和 Data 都可以给组件提供数据。

Props 的数据是由组件外部提供,不能直接在组件内进行修改,遵循单向数据流。data 组件内部提供的数据,可以直接在组件内部进行修改。

什么是单向数据流?

数据只能由父级 Prop 的数据更新,会向下流动,影响子组件。子组件没办法通过修改 Props 传递的数据从而影响父组件的状态,数据只能由父到子单向传递。

  • props 是组件的属性,它们是从父组件传递到子组件的数据。props是只读的,这意味着你不能在子组件内部修改它们。如果你尝试这样做,Vue将会警告你。这是因为改变props可能会导致应用的状态变得难以理解和追踪。所以,如果你需要根据props的值来改变一些东西,你应该使用计算属性或者在data中复制该prop。
  • data 是组件的内部状态,它包含的是组件自己需要的数据。与props不同,data是组件内部的,不通过外部输入。data是响应式的,意味着如果你改变了data的值,那么使用这个值的地方也会更新。并且,每个组件实例都有自己的data对象,所以改变一个组件的data不会影响其他组件。

📌 Vue 组件 通信 v-model:

实现 父↔子 之间相互通信。

v-model 的本质:

html 复制代码
<!-- 使用v-model指令 -->
<input type="text" v-model="userName">

<!-- v-model的本质是下面这行代码 -->
<input 
  type="text" 
  :value="userName" 
  @input="userName =(<HTMLInputElement>$event.target).value"
>
  • ($event.target)这个是ts的类型断言,target一定是html元素而不为空
  • 数据到页面 :value="userName"
  • 页面到数据 @input="userName =(<HTMLInputElement>$event.target).value"

组件标签上的v-model的本质::moldeValueupdate:modelValue事件

html 复制代码
<!-- 组件标签上使用v-model指令 -->
<AInput v-model="userName"/>

<!-- 组件标签上v-model的本质 -->
<AInput :modelValue="userName" @update:model-value="userName = $event"/>

在vue3中:

  • 数据到页面 :modelValue="userName"
  • 页面到数据 @update:model-value="userName = $event"

AInput组件中:

html 复制代码
<template>
  <div class="box">
    <!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 -->
	<!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件-->
    <input 
       type="text" 
       :value="modelValue" 
       @input="emit('update:model-value',$event.target.value)"
    >
  </div>
</template>

<script setup lang="ts" name="AtguiguInput">
  // 接收props
  defineProps(['modelValue'])
  // 声明事件
  const emit = defineEmits(['update:model-value'])
</script>

也可以更换value,例如改成abc:

html 复制代码
<!-- 也可以更换value,例如改成abc-->
<AInput v-model:abc="userName"/>

<!-- 上面代码的本质如下 -->
<AInput :abc="userName" @update:abc="userName = $event"/>

AInput组件中:

html 复制代码
<template>
  <div class="box">
    <input 
       type="text" 
       :value="abc" 
       @input="emit('update:abc',$event.target.value)"
    >
  </div>
</template>

<script setup lang="ts" name="AtguiguInput">
  // 接收props
  defineProps(['abc'])
  // 声明事件
  const emit = defineEmits(['update:abc'])
</script>

如果value可以更换,那么就可以在组件标签上多次使用v-model

html 复制代码
<AtguiguInput v-model:abc="userName" v-model:xyz="password"/>

关于$event到底是什么?什么时候能够.target

  • 对于原生事件,$event就是事件对象====>能.target
  • 对于自定义事件,$event就是触发事件时,所传递的对象====>不能.target

📌 Vue 组件 通信 $attrs:

概述:$attrs用于实现当前组件的父组件 ,向当前组件的子组件 通信(祖→孙)。

具体说明:$attrs是一个对象,包含所有父组件传入的标签属性。

注意:$attrs会自动排除props中声明的属性(可以认为声明过的 props 被子组件自己"消费"了)

父组件:

html 复制代码
<template>
  <div class="father">
    <h3>父组件</h3>
		<Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/>
  </div>
</template>

<script setup lang="ts" name="Father">
	import Child from './Child.vue'
	import { ref } from "vue";
	let a = ref(1)
	let b = ref(2)
	let c = ref(3)
	let d = ref(4)

	function updateA(value){
		a.value = value
	}
</script>

子组件:

html 复制代码
<template>
	<div class="child">
		<h3>子组件</h3>
		<GrandChild v-bind="$attrs"/>
	</div>
</template>

<script setup lang="ts" name="Child">
	import GrandChild from './GrandChild.vue'
</script>

孙组件:

html 复制代码
<template>
	<div class="grand-child">
		<h3>孙组件</h3>
		<h4>a:{{ a }}</h4>
		<h4>b:{{ b }}</h4>
		<h4>c:{{ c }}</h4>
		<h4>d:{{ d }}</h4>
		<h4>x:{{ x }}</h4>
		<h4>y:{{ y }}</h4>
		<button @click="updateA(666)">点我更新A</button>
	</div>
</template>

<script setup lang="ts" name="GrandChild">
	defineProps(['a','b','c','d','x','y','updateA'])
</script>

📌 Vue 组件 通信 refs、parent:

概述:

  • $refs用于 :父→子。
  • $parent用于:子→父。

原理如下:

属性 说明
$refs 值为对象,包含所有被ref属性标识的DOM元素或组件实例。
$parent 值为对象,当前组件的父组件实例对象。

子组件需要将数据暴露出来,父组件才能被允许使用;同样的,父组件把需要子组件操作的数据暴露出来,子组件才能拿到使用。

// 宏函数把数据交给外部
defineExpose({ toy, book })

📌 Vue 组件 通信 provide、inject:

概述:实现祖孙组件直接通信

具体使用:

  1. 在祖先组件中通过provide配置向后代组件提供数据
  2. 在后代组件中通过inject配置来声明接收数据

具体编码:

【第一步】父组件中,使用provide提供数据

html 复制代码
<template>
  <div class="father">
    <h3>父组件</h3>
    <h4>资产:{{ money }}</h4>
    <h4>汽车:{{ car }}</h4>
    <button @click="money += 1">资产+1</button>
    <button @click="car.price += 1">汽车价格+1</button>
    <Child/>
  </div>
</template>

<script setup lang="ts" name="Father">
  import Child from './Child.vue'
  import { ref,reactive,provide } from "vue";
  // 数据
  let money = ref(100)
  let car = reactive({
    brand:'奔驰',
    price:100
  })
  // 用于更新money的方法
  function updateMoney(value:number){
    money.value += value
  }
  // 提供数据
  provide('moneyContext',{money,updateMoney})
  provide('car',car)
</script>

注意:子组件中不用编写任何东西,是不受到任何打扰的

【第二步】孙组件中使用inject配置项接受数据。

html 复制代码
<template>
  <div class="grand-child">
    <h3>我是孙组件</h3>
    <h4>资产:{{ money }}</h4>
    <h4>汽车:{{ car }}</h4>
    <button @click="updateMoney(6)">点我</button>
  </div>
</template>

<script setup lang="ts" name="GrandChild">
  import { inject } from 'vue';
  // 注入数据
 let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(x:number)=>{}})
  let car = inject('car')
</script>

📌 Vue3 组件通信方式:

Vue3组件通信和Vue2的区别:

  • 移除了事件总线,使用 mitt代替。
  • vuex 换成了 pinia
  • .sync 优化到了 v-model 里面了。
  • $listeners 所有的东西,合并到$attrs中了。
  • $children 被砍掉了。

常见搭配形式:

这张表中的其他的通信方式,例如插槽,Pinia等等,会在后续的文章中进行详细的学习和解析,这里我们只需要了解 Vue 原生提供的常用组件之间的通信方式即可!


📌 往期文章:

2024年还在问前端怎么学?一份前端学习指南_web前端怎么学习人工智能-CSDN博客

【前端学习指南】基础开发环境搭建_前端项目搭建环境-CSDN博客

【前端学习指南】开启 Vue 的学习之旅_前端vue 学习之旅-CSDN博客

【前端学习指南】第一站 Vue 生命周期初探_vue 前端学习-CSDN博客

【前端学习指南】第二站 Vue 工程化开发-CSDN博客

【前端学习指南·番外篇】Node.js 安装及环境配置_node前端运行环境-CSDN博客


📌 今日一言:

一天啥也不是,就知道敲代码!

相关推荐
学不会•22 分钟前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS1 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
Theodore_10221 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
活宝小娜3 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点3 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow3 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o3 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
----云烟----3 小时前
QT中QString类的各种使用
开发语言·qt
lsx2024063 小时前
SQL SELECT 语句:基础与进阶应用
开发语言
开心工作室_kaic4 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端