深入了解 Vue 3 组件间通信机制

什么是组件?

在 Vue3 中,组件是构建应用界面的核心概念之一。组件可以看作是可复用、自包含和可组合的代码块,用于封装 UI 元素和相应的行为逻辑。

通俗来说就是,组件(Component)是一种对数据和方法的简单封装,每一个组件有自己单独的逻辑,并且可以分别管理。不同的组件组合在一起,就形成了页面。所以,每一个Web页面可以抽象成是不同组件组合而成的,页面只是这些组件的一个容器。并且这些组件可以在不影响程序运行的情况下,随时被替换,利用这种组件化的思想可以将一个巨大的东西拆分成很多小模块,也是现代前端框架的核心思想之一。

在 Vue 中,通常一个应用会以一颗嵌套的组件树的形式来组织,如图所示:

img

上图中将整体页面为一个根组件,然后根组件下有三个子组件,分别是页头组件、内容区组件和侧边栏组件。在内容区组件下,又细分出两个内容组件,而侧边栏组件则有三个侧边栏内容组件。所有组件整齐排列,按照树形结构组合,这就是 Vue 内组件的组织结构。

另外 Vue 3 的组件还具有以下特点:

  1. 模块化:组件以模块化的方式进行定义和使用,每个组件都有独立的作用域,使得代码更加结构化和可维护。组件可根据需要进行拆分,形成层次化的组件树结构。

  2. 复用性:组件是可复用的,可以在应用中多次使用。通过将 UI 元素和相关的逻辑封装为组件,可以避免代码重复,并且可以轻松地在不同的上下文中重用组件。

  3. 可组合性:组件可以通过父子组件之间的嵌套与组合,形成更大规模的应用界面。通过传递属性 prop 和监听事件 emit,组件之间可以进行数据和通信的交互。

  4. 响应式:组件内部的数据可以使用 Vue 3 的响应式系统进行管理,当数据发生变化时,组件会自动更新视图。通过响应式系统,可以实现数据的双向绑定和自动渲染。

  5. 生命周期钩子函数:Vue 3 的组件具有一系列的生命周期钩子函数,用于在组件不同的生命周期阶段执行特定的代码逻辑。生命周期钩子函数可以帮助开发者控制组件的行为和实现特定的功能。

  6. 单文件组件:Vue 3 支持使用单文件组件(.vue 文件)的方式来定义和编写组件。单文件组件将组件的模板、样式和逻辑都封装在一个文件中,提高了代码的可读性和维护性。

组件定义

一个 Vue 组件的结构通常包括三个部分:模板(Template)、脚本(Script)和样式(Style)。这些部分一般会放在一个单文件组件(.vue 文件)中,也可以分离成三个独立的文件。

下面是一个典型的 Vue 组件的结构示例:

<template>
  <!-- 模板部分 -->
</template>

<script setup>
  // 脚本部分
</script>

<style>
  /* 样式部分 */
  /* CSS 样式规则 */
</style>

其中:

  1. 模板(Template):模板部分定义了组件的结构和布局,使用 HTML 和 Vue 的模板语法编写。在模板中可以插入动态数据和表达式,并通过指令(如 v-bindv-if)和事件绑定等方式与组件的数据和行为进行交互。

  2. 脚本(Script):脚本部分是组件的逻辑核心,使用 JavaScript 或 TypeScript 编写。在脚本中,可以定义组件的属性、计算属性、方法、生命周期钩子函数等。通过脚本,可以处理组件的数据逻辑、事件响应等功能。

  3. 样式(Style):样式部分定义了组件的样式规则,使用 CSS 或预处理器(如 Sass、Less)编写。可以为组件元素添加类名、样式选择器等,来对组件进行样式修饰和美化。

这种将模板、脚本和样式封装在一个单文件组件中的方式,可以提高代码的可读性和维护性,并且使得组件的结构更加清晰和独立。通过单文件组件,我们可以更好地组织和管理组件的相关资源,并方便地重用和维护组件。

组件注册

在 Vue 中,如果要使用自定义组件,第一步需要做的就是将其注册到应用中。Vue 组件的注册可以有两种方式:全局注册和局部注册。

  1. 全局注册:全局注册的组件可以在整个应用的任何地方使用,无需额外的导入或注册操作。

    全局注册组件的方法是在 main.ts 文件中使用 app.component 方法。例如,将自定义组件 my-component 进行全局注册:

    import { createApp } from 'vue';
    import App from './App.vue';
    import MyComponent from './components/MyComponent.vue';
    
    const app = createApp(App);
    
    app.component('my-component', MyComponent);
    
    app.mount('#app');
    

    在上述示例中,我们首先使用 createApp 创建应用实例,并将根组件 App 作为参数传入。然后,使用 app.component 方法全局注册名为 my-component 的组件,并将其与 MyComponent 组件关联。最后,通过 app.mount 将应用挂载到页面中的 DOM 元素上。

    在全局注册后,我们可以在任何组件的模板中使用 <my-component></my-component> 标签来引入和使用该组件,无需在局部组件中重新注册。

  2. 局部注册:局部注册的组件只能在其所属的组件内部使用,无法在其他组件(包括子组件)中直接使用。

    在组合式 API 中组件局部注册时直接引入,然后直接调用即可。例如:

    <script setup>
    import MyComponent from './MyComponent.vue';
      
    </script>
    
    <template>
        <!-- 使用局部注册的子组件 -->
        <my-component></my-component>
    </template>
    

    在上述代码中,<script setup> 部分引入了 MyComponent 组件,并且不需要使用 app.component 进行额外的局部注册。直接在模板中使用 <my-component></my-component> 即可使用这个局部注册的组件。

通过全局或局部注册组件,我们可以在应用中灵活地使用和组织组件,实现不同层次的封装和复用。

组件通信

在 Vue 中,组件之间的通信,大致可以分为以下四种情况:父组件向子组件通信、子组件向父组件通信、父组件向隔代子组件通信和非父子组件之间的通信。如图:

未命名文件 (3)

四种通信模式涵盖了 Vue 中组件和组件通信的所有情况,解决方法如下:

  • 父组件向子组件传递数据:使用组件的 props 实现。
  • 子组件向父组件传递数据:通过自定义事件实现。
  • 父组件向隔代子组件传递数据:使用订阅发布实现,使用 provide/inject 在父组件提供数据,在后代组件中注入数据。
  • 非父子组件之间传递数据:使用第三方状态管理实现,如 Pinia。

根据不同的场景和需求,选择适合的通信方式来进行组件之间的通信。

使用 Props 通信

Props 关键字代表开发者在组件上注册的一些自定义属性,然后父组件通过子组件的 Props 属性将数据直接传递到子组件内部,供子组件调用处理。

需要注意的是,Props 传递的数据全部为单向流数据,即只能从父组件向子组件传递,不能子组件向父组件传递。所以,在使用 Props 进行组件之间通信时,需要首先在子组件内定义 Props,然后在父组件内调用,代码如下:

//子组件 ChildComponent.vue
<template>
  <p>{{ message }}</p>
</template>

<script setup lang="ts">
  defineProps<{
    message?:string
  }>()
</script>

在子组件中,使用 defineProps() 方法定义了一个名为 message 的 prop,类型为字符串。通过在模板中使用双花括号语法 {{ message }},可以显示接收到的来自父组件的数据。

//父组件 Parent.vue
<script setup lang="ts">
import ChildComponent from './ChildComponent.vue'

const parentMessage=ref<string>("Hello from parent component!")
</script>

<template>
<child-component :message="parentMessage"></child-component>
</template>

在父组件中,定义了一个名为 parentMessage 的数据属性,并将其绑定到子组件的 prop 上。通过在子组件标签上使用冒号语法 :message="parentMessage",将父组件的 parentMessage 数据传递给子组件的 message prop。

使用以上代码,父组件就能够通过 props 向子组件传递数据了。在子组件中,可以直接使用该数据进行展示或进一步处理。这种父组件向子组件传递数据的方式非常常见,并且在 Vue 组件开发中被广泛使用。

另外,如果想给 Props 定义的属性一个初始值,可以使用 withDefaults() 方法,代码如下:

<script setup lang="ts">
  withDefaults(defineProps<{message?:string}>(),{
    message:'这是默认值'
  })
</script>

withDefaults() 方法接收的第一个参数是 Props 定义,第二个参数则是这些 Props 定义的属性的默认值。并且这里在定义 Props 的时候,采用 TypeScript 的可选属性定义组件属性,即再属性名称后面添加问号,代表当前 Props 属性为非必传属性,子组件使用默认值来进行渲染。

使用自定义事件通信

在 Vue 3 的组合式 API 中,组件的自定义事件是通过 defineEmits() 方法定义。然后在需要被调用的地方发射自定义事件给父组件,同时,父组件通过 v-on 指令将本身的处理函数与子组件发射的自定义事件相绑定,从而实现子组件向父组件通信的过程。

下面是一个示例代码:

//子组件 ChildComponent.vue
<template>
<button @click="emit('btnClick', message)">传递</button>
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  const message = ref<string>("子组件消息")
  const emit = defineEmits<{
    (eventName: 'btnClick', message: string): void
  }>()
</script>

上述代码中,我们在 <script setup> 中,使用 defineEmits 来定义了一个 btnClick 的自定义事件。然后,我们将 sendMessage 方法进行了修改,使其调用 emit 方法并传递了 btnClick 事件名称和 message 变量的值。

这样,当子组件的按钮被点击时,就会触发 btnClick 事件,并将 message 的值传递给父组件。父组件可以监听该事件并进行相应的处理。

//父组件 Parent.vue
<script>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const message = ref<string>("父组件消息")
const handle = (msg:string):void=>{
    message.value=msg
}  
</script>

<template>
    <p>消息: {{ message }}</p>
    <child-component @btn-click="handle"></child-component>
</template>

在父组件中,我们使用 @btn-click 监听子组件触发的 btn-click 自定义事件,并将 handle 方法指定为事件处理函数。

当子组件中的按钮被点击时,会触发 btn-click 事件并将消息作为参数传递给父组件的 handle 方法。在 handle 方法中,我们将接收到的消息更新到 message 变量上,从而实现父组件接收子组件发送的消息并进行处理的效果。

使用订阅发布通信

Props 属性负责父组件向子组件传递数据,子组件可以通过自定义事件向父组件传递数据。但是如果两个组件不是父子组件关系,而是深度嵌套的组件,并且深层子组件只需要父组件的部分内容,这个时侯如果使用 Props 属性逐级传下去,将会显得非常麻烦而且容易出错。针对这种情况,Vue 推出了发布订阅进行通信,即 Provider/Inject 通信。

Provider/Inject 通信,需要有一个 Provider 和一个或者多个 Inject。在父组件中,Provider 负责提供数据,深层子组件里的 Inject 负责读取数据。这种通信方式,不管父子组件中间相隔多久,都是可以实现的。

例如这里的组件层级关系如下:

ProjectInjectComponent
 ﹂ProjectInjectChild
  ﹂ProjectInjectGrandson

在 ProjectlnjectComponent 中使用 Provide 先发布一条数据, 然后在孙组件 ProjectInjectGrandson 中通过 Inject 订阅这条数据并显示。代码如下:

//ProjectInjectComponent.vue(父组件)
<script lang="ts" setup>
import { provide, ref } from 'vue';
import ProjectInjectChild from './ProjectInjectChild.vue';

const message = ref<string>("ProjectInjectComponent 组件消息")

//发布数据,key:message
provide('message',message);
</script>

<template>
    <ProjectInjectChild></ProjectInjectChild>
</template>
//ProjectInjectChild.vue(子组件)
<script lang="ts" setup>
import ProjectInjectGrandson from './ProjectInjectGrandson.vue';

</script>

<template>
    <ProjectInjectGrandson></ProjectInjectGrandson>
</template>
//ProjectInjectGrandson.vue(孙组件)
<script lang="ts" setup>
import { inject, ref } from 'vue';

//订阅数据 key:message,defaultValue:"消息"
const message = inject<string>('message',"消息")
</script>

<template>
    <h2>{{ message }}</h2>
</template>

在 ProjectInjectComponent 组件中通过 Provide 发布了一个名为 message 数据;在嵌套最底层的 ProvideInjectGrandson 组件中通过 Inject 读取父组件发布的 message 数据,并渲染在页面上。因为父组件中的 message 是响应式数据,所以父组件中的数据会同步更新到嵌套的子组件中,最终将更新后的数据重新渲染到页面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/72318.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c语言经典例题讲解(输出菱形,喝汽水问题)

目录 一、输出菱形 二、喝汽水问题 方法1&#xff1a;一步一步来 方法二&#xff1a;直接套公式 一、输出菱形 输出类似于下图的菱形&#xff1a; 通过分析&#xff1a;1、先分为上下两部分输出 2.在输出前先输出空格 3.找规律进行输出 可知&#xff0c;可令上半部分lin…

Vue3 + Ts + Vite 封装一套企业级axiso全流程

前期回顾 从零搭建 Vue3 VIte Ts 项目 —— 并集成eslint 、prettier、stylelint、husky、lint-staged、pinia、axios、loding、动态路由…_彩色之外的博客-CSDN博客 实现功能&#xff1a; 取消重复请求&#xff1a;完全相同的接口在上一个pending状态时&#xff0c;自动取…

C语言库函数之 qsort 讲解、使用及模拟实现

引入 我们在学习排序的时候&#xff0c;第一个接触到的应该都是冒泡排序&#xff0c;我们先来复习一下冒泡排序的代码&#xff0c;来作为一个铺垫和引入。 代码如下&#xff1a; #include<stdio.h>void bubble_sort(int *arr, int sz) {int i 0;for (i 0; i < sz…

python爬虫实战(2)--爬取某博热搜数据

1. 准备工作 使用python语言可以快速实现&#xff0c;调用BeautifulSoup包里面的方法 安装BeautifulSoup pip install BeautifulSoup完成以后引入项目 2. 开发 定义url url https://s.微博.com/top/summary?caterealtimehot定义请求头&#xff0c;微博请求数据需要cookie…

推荐 4 个 yyds 的 GitHub 项目

本期推荐开源项目目录&#xff1a; 1. 开源的 Markdown 编辑器 2. MetaGPT 3. SuperAGI 4. 一个舒适的笔记平台 01 开源的 Markdown 编辑器 Cherry 是腾讯开源的 Markdown 编辑器&#xff0c;基于 Javascript具有轻量简洁、易于扩展等特点&#xff0c; 它可以运行在浏览器或服…

Java基础入门篇——数组初识

一、数组 1.假设某公司有100个员工&#xff0c;需要统计某公司员工的工资情况&#xff0c;首先需要声明100个变量来分别记每个员工的工资&#xff0c;那么如果按照之前的做法&#xff0c;可能定义的结构如下所示&#xff1a; int a1,a2,a3,......a100; 要求你输出这100个员工…

Qt中将信号封装在一个继承类中的方法

QLabel标签类对应的信号如下&#xff1a; Qt中标签是没有双击&#xff08;double Click&#xff09;这个信号的&#xff1b; 需求一&#xff1a;若想双击标签使其能够改变标签中文字的内容&#xff0c;那么就需要自定义一个“双击”信号&#xff0c;并将其封装在QLabel类的派生…

【2023新教程】树莓派4B开机启动-树莓派第一次启动-树莓派不使用显示器启动-树莓派从购买到启动一步一步完全版!

背景 闲来无事&#xff0c;在咸鱼上买了一个树莓派4B。买来配件都十分齐全&#xff0c;于是就想着启动来测试一下。下面是树莓派无显示器第一次启动的全过程&#xff0c;包含安装系统。 网上的教程大多需要额外使用显示器、鼠标、键盘之类的外设。然而&#xff0c;树莓派本身就…

“可一学院”区块链学习平台正式启动,助力BSV技术普及与传播

2023年8月8日&#xff0c;上海可一澈科技有限公司&#xff08;以下简称“可一科技”&#xff09; 正式发布区块链学习平台“可一学院”。“可一学院” 立足于BSV区块链技术本源&#xff0c;汇集了多层次的专业课程和学习资源&#xff0c;致力于打造一个适合各类人群使用的一站式…

因果推断(三)双重差分法(DID)

因果推断&#xff08;三&#xff09;双重差分法&#xff08;DID&#xff09; 双重差分法是很简单的群体效应估计方法&#xff0c;只需要将样本数据随机分成两组&#xff0c;对其中一组进行干预。在一定程度上减轻了选择偏差带来的影响。 因果效应计算&#xff1a;对照组y在干预…

谈谈语音助手

目录 1.什么是语音助手 2.语音助手的发展过程 3.现在有哪些成熟的语音助手 4.语音助手对人类发展的影响 1.什么是语音助手 语音助手是一种能够通过语音交互与用户进行沟通和执行任务的虚拟助手。它基于人工智能和自然语言处理技术&#xff0c;能够理解用户的语音指令&#x…

[数据集][目标检测]骑电动车摩托车不戴头盔数据集VOC格式1385张

数据集格式&#xff1a;Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件&#xff0c;仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数)&#xff1a;1385 标注数量(xml文件个数)&#xff1a;1385 标注类别数&#xff1a;2 标注类别名称:["y","n&q…

最强自动化测试框架Playwright(22)-模拟器

可以使用测试生成器通过仿真生成测试&#xff0c;以便为特定窗口、设备、配色方案生成测试&#xff0c;以及模拟地理位置、语言或时区。测试生成器还可以生成测试&#xff0c;同时保留经过身份验证的状态。 模拟视口大小 Playwright 打开一个浏览器窗口&#xff0c;其视口设置…

C语言——将一串字符进行倒序

//将一串字符进行倒序 #include<stdio.h> #define N 6 int main() {int a[N]{0,1,2,3,4,5};int i,t;printf("原数组数值&#xff1a; ");for(i0;i<N;i)printf("%d",a[i]);for(i0;i<N/2;i){ta[i];a[i]a[N-1-i];a[N-1-i]t;}printf("\n排序…

掌握Python的X篇_27_Python中标准库文档查阅方法介绍

前面的博文介绍了python的基本语法、模块及其导入方法。前人将各种方法封装成模块、库、函数供我们使用&#xff0c;如何去使用前人做好的东西&#xff0c;那就需要去查阅文档。今天就介绍python中官方文档的查阅方式。对于初学者而言&#xff0c;python自带的文档就已经足够好…

小白带你部署LNMP分布式部署

目录 前言 一、概述 二、LNMP环境部署 三、配置nginx 1、yum安装 2、编译安装 四、安装 1、编译安装nginx 2、网络源 3、稍作优化 4、修改配置文件vim /usr/local/nginx/conf/nginx.conf 5、书写测试页面 五、部署应用 前言 LNMP平台指的是将Linux、Nginx、MySQL和…

Python爬虫——requests_cookie登陆古诗文网

寻找登陆需要的参数 __VIEWSTATE:aiMG0UXAfCzak10C7436ZC/RXoZbM2lDlX1iU/4wjjdUNsW8QUs6W2/3M6XIKagQZrC7ooD8Upj8uCnpQMXjDAp6fS/NM2nGhnKO0KOSXfT3jGHhJAOBouMI3QnlpJCQKPXfVDJPYwh169MGLFC6trY __VIEWSTATEGENERATOR: C93BE1AE from: http://so.gushiwen.cn/user/collect.…

Java并发编程(五)线程同步 下 [CAS/原子类/同步容器类/同步工具类]

CAS 概述 CAS全称为Compare-And-Swap。它是一条CPU的原子指令,是硬件对于并发操作共享数据的支持。其作用是CPU在某个时刻比较两个值是否相等 核心原理&#xff1a;在操作期间CAS先比较下主存中的值和线程中工作内存中的值是否相等,如果相等才会将主存中的值更新为新值&…

3.2 Tomcat基础

1. Tomcat概述 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器。 Tomcat版本&#xff1a;apache-tomcat-8.5.76。 2.IDEA集成Tomcat 第一步 第二步 第三步 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff0…

【JavaEE进阶】SpringBoot 配置文件

文章目录 SpringBoot配置文件1. 配置文件的作用2. 配置文件的格式3. properties 配置文件说明3.1 properties 基本语法3.2 读取配置文件3.3 properties 优缺点分析 4. yml配置文件说明4.1 yml 基本语法4.2 yml 配置读取 5. properties和yml的对比 SpringBoot配置文件 1. 配置文…