【vue3】07-vue组件之间的通信-父子互传-事件总线

news2024/12/24 10:16:24

文章目录

  • Vue的组件嵌套
  • Vue组件化-组件间通信
    • 父子组件之间通信
    • 非父子组件间的通信

Vue的组件嵌套

前面我们是将所有的逻辑放到一个App.vue中:

  • 在之前的案例中,我们只是创建了一个组件App;
  • 如果我们一个应用程序将所有的逻辑都放在一个组件中,那么这个组件就会变成非常的臃肿和难以维护;
  • 所以组件化的核心思想应该是对组件进行拆分,拆分成一个个小的组件;
  • 再将这些组件组合嵌套在一起,最终形成我们的应用程序;

观察以下代码,我们可以发现:

  1. 将所有的代码逻辑全部放到一个组件中,代码是非常的臃肿和难以维护的。
  2. 并且在真实开发中,我们会有更多的内容和代码逻辑,对于扩展性和可维护性来说都是非常差的。
  3. 所以,在真实的开发中,我们会对组件进行拆分,拆分成一个个功能的小组件。
<template>
  <div id="app">
    <div>
		<h2>我是标题</h2>
	    <div class="top">topbanner</div>
	    <div class="nav">nav</div>
    </div>
    
    <div>
      <h2>content</h2>
	    <ul>
	      <li>商品列表1</li>
	      <li>商品列表2</li>
	      <li>商品列表3</li>
	      <li>商品列表4</li>
	      <li>商品列表5</li>
	    </ul>
    </div>
    
    <div>
      <h2>Footer</h2>
      <h2>免责声明</h2>
    </div>
  </div>
</template>

大体上,我们可以按照如下方式拆分

在这里插入图片描述

像上面的代码通过拆分后,我们只需要在App根组件中去编写对应的组件:

<template>
<div id="app">
<!-- 组件嵌套:形成组件数,组件中又有局部组件,各个父子组件关系 -->
<app-header></app-header>
<AppContent></AppContent>
<AppFotter></AppFotter>
</div>
</template>


<script>
import AppHeader  from "./components/AppHeader.vue";
import AppContent  from "./components/AppContent.vue";
import AppFotter  from "./components/AppFooter.vue";

export default {
  components:{
    AppHeader,
    AppContent,
    AppFotter,
  }
}
</script>

<style scoped>

</style>


上面的嵌套逻辑有如下关系:

  • App组件是Appheader组件,AppContent组件,AppFooter组件的父组件;
  • Main组件是Banner、ProductList组件的父组件
  • 还可以将AppHeader组件中的top和nav部分抽取为子组件;

在开发过程中,我们会经常遇到需要组件之间相互进行通信:

  • 比如App可能使用了多个Header,每个地方的Header展示的内容不同,那么我们就需要使用者传递给Header一些数据,让其进行展示;
  • 又比如我们在Main中一次性请求了Banner数据和ProductList数据,那么就需要传递给它们来进行展示;
  • 也可能是子组件中发生了事件,需要由父组件来完成某些操作,那就需要子组件向父组件传递事件

总之,在一个Vue项目中,组件之间的通信是非常重要的环节,所以接下来我们就具体学习一下组件之间是如何相互之间传递数据的:


Vue组件化-组件间通信

父子组件之间通信

在这里插入图片描述


(1)父传子:父组件可以通过props向子组件传递属性,然后子组件可以在其模板中使用这些属性。

Props方式一:数组用法

例如我们有如下的一个根组件, 用于展示用户信息, 但是由于子组件中的数据时固定的, 我们展示的两个信息是相同的:

  • 这时候就需要父组件通过自定义的attribute, 将数据传递给子组件:
<template>
    <!-- 父传子:传递参数 -->
    <!-- 展示信息-->
    <ShowInfo name="kobe" age="25" height="1.89"></ShowInfo>
</template>
    
    
    <script>
 import ShowInfo from "../02_组件通信父传子/ShowInfo.vue";
    
    export default {
      components:{
        ShowInfo
      },

    }
    </script>
  • 父组件通过自定义attribute将数据传递给子组件, 子组件可以通过props接收:
<template>
    <!-- 1.展示sevgilid的个人信息 -->
    <div class="infos">
       <h2>姓名:{{ name }}</h2>
       <h2>年龄:{{ age }}</h2>
       <h2>身高:{{ height }}</h2>
    </div>
       </template>
       
       
       <script>       
       export default {
        // 接收父组件传递过来的参数
      	//   1.props数组语法
        //    -弊端:(1)不能对类型进行验证(2)没有默认值
        props:["name","age","height"]
	    }
       </script>

props父传子数组方式效果示例:

在这里插入图片描述


Props方式二:对象用法

在数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制

当使用对象语法的时候,我们可以对传入的内容限制更多(重点掌握):

  • 比如通过type指定传入的attribute的类型;
  • 比如通过require指定传入的attribute是否是必传的;
  • 比如通过default指定没有传入时,attribute的默认值;
<template>
    <!-- 1.父传子:传递参数 -->
    <!-- 为age加上:将父组件的age属性绑定到子组件的age属性上 -->
    <ShowInfo name="kobe" :age="25" height="1.89" abc="bca"></ShowInfo>
    <!-- 如果当前的属性是一个非props的attribute(属性),那么该属性会默认添加到子组件的根元素上:如此处的abc -->
    <ShowInfo name="ysl" :age="23" height="1.68"></ShowInfo>
    
     <!-- 2.不传递参数时展示默认信息: -->
    <ShowInfo></ShowInfo>
</template>
    
    
    <script>
 import ShowInfo from "../02_组件通信父传子/ShowInfo.vue";
    
    export default {
      components:{
        ShowInfo
      },

    }
    </script>
  • 子组件中使用对象的方式接收父组件传递的数据:
<template>
   <!-- 1.展示sevgilid的个人信息 -->
   <div class="infos">
      <h2>姓名:{{ name }}</h2>
      <h2>年龄:{{ age }}</h2>
      <h2>身高:{{ height }}</h2>
   </div>
</template>
       
       
<script>


export default {
   // 2.props对象语法(掌握)
   props: {
      name: {
         type: String,
         // 必传参,加上之后
         // required:true,
         default: "我是默认name"
      },
      age: {
         type: Number,
         default: 0

      },
      height: {
         type: Number,
         default: 2
      },
      friend: {
         type: Object,
         // 如果是对象类型,这里必须是函数
         default: () => ({ name: "james" })
      },
      hobbies: {
         // 数组类型,默认值也写为函数
         type: Array,
         default() {
            return ["唱", "跳", "rap"]
         }
      }
   }

}
</script>

props父传子对象方式效果示例:

在这里插入图片描述


补充一:

type可以指定多种类型:String,Number,Boolean,Array,Object,Date,Function,Symbol;

补充二:

当type指定类型为对象类型和数组类型时,默认值需要写为一个函数,如下:

	Props:{
		      friend: {
		         type: Object,
		         // 如果是对象类型,这里必须是函数
		         default: () => ({ name: "james" })
		      },
	}


(2)子传父:子组件可以使用$emit向父组件发送事件,然后父组件可以侦听这些事件并对它们做出响应。

使用场景:

  • 当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容;
  • 子组件有一些内容想要传递给父组件的时候;

例如:

  1. 我们可以在子组件中创建一个自定义事件,然后在触发时使用emit将数据发送给父组件。
<template>
  <button @click="handleClick">Click me</button>
</template>

<script>
export default {
  methods: {
    handleClick() {
      this.$emit('button-clicked', 'Hello, World!')
    }
  }
}
</script>
  1. 父组件中,我们可以使用@事件名来侦听这个事件,然后在父组件的方法中处理这些数据:
<template>
  <my-button @button-clicked="handleButtonClicked"></my-button>
</template>

<script>
import MyButton from './MyButton.vue'

export default {
  components: {
    MyButton
  },
  methods: {
    handleButtonClicked(data) {
      console.log(data) // 输出 Hello, World!
    }
  }
}
</script>

再写一个计数器案例来练习子传父的操作:

  • 计数器可以+1, +5, +10, -1, -5, -10, 我们将加的功能抽离成一个组件, 将减的功能抽离一个组件
  • 当我们子组件发生了加或者减的事件, 如何让父组件监听到呢, 这时候就需要使用子组件发生自定义事件给父组件监听
  • 子组件事件触发之后, 通过 this.$emit的方式发出去事件;

addBtn.vue:

<template>
    <div class="add">
    <button @click="addClick(1)">+1</button>
    <button @click="addClick(5)">+5</button>
    <button @click="addClick(10)">+10</button>
  </div>
</template>
    
    
    <script>

    
    export default {
        methods:{
            addClick(count){
                console.log(count);
                this.$emit("add",count)
            }
        }
  
    }
    </script>

subBtn.vue:

<template>
    <div class="sub">
        <button @click="subClick(1)">-1</button>
        <button @click="subClick(5)">-5</button>
        <button @click="subClick(10)">-10</button>
    </div>
</template>
    
    
<script>


export default {
    // // 1.emits的数组语法:自定义事件时,在此处注册说明一下,再见他事件时就有提示
    // emits:["sub"]:常用,

    // 2.emits的对象语法(vue3当中可以对传递的参数就行验证(了解))
    emits: {
        sub: function (count) {
            if (count < 10) {
                return true
            }
            return false
        }
    },
    methods: {
        subClick(count) {
            console.log(count);
            // 让子组件发出去一个自定义事件(事件名称,参数)
            this.$emit("sub", count)
        }
    }

}
</script>

App.vue:父组件监听子组件发出的自定义事件, 然后执行对应的操作

<template>
    <div id="app">
        <h2>计数器:{{ counter }}</h2>
        <add-btn @add="addCounter"></add-btn>
        <SubBtn @sub="subCounter"></SubBtn>
    </div>
</template>
    
    
<script>
import AddBtn from "./components/AddBtn.vue";
import SubBtn from "./components/SubBtn.vue";

export default {
    data() {
        return {
            counter: 0
        }
    },
    components: {
        AddBtn,
        SubBtn
    },
    methods: {
        addCounter(count) {
            this.counter += count;
        },
        subCounter(count) {
            this.counter -= count;
        }
    }

}
</script>

补充:

  • 在Vue3中,提供了一个emits API;emits选项是一个数组,用于在组件中注册自定义事件。
  • 当子组件触发自定义事件时,父组件可以接收到事件并做出响应。声明emits选项后,会为组件自动添加$emit方法,并在使用时进行类型检查和语法提示,方便使用和开发。
  • 例如,我们可以在组件中声明一个emits选项,然后在模板中使用它触发一个名为"test"的事件:
export default {
  emits: ['test'],  // 声明事件名称
  methods: {
    handleClick() {
      this.$emit('test', 'Hello, World!')  // 触发test事件
    }
  }
}

在模板中触发test事件时,代码编辑器会自动提示出test事件名称,方便开发者使用。


非父子组件间的通信

  • 此章讲解的是, 在学习状态管理Vuex和Pinia之前, 非父子间通信的方案;
  • Vuex和Pinia是Vue官方提供的状态管理库,用于管理应用程序中的所有数据

在开发中,我们构建了组件树之后,除了父子组件之间的通信之外,还会有非父子组件之间的通信

这里我们主要讲两种方式:

  • Provide/Inject
  • 全局事件总线

(1)Provide和Inject

Provide/Inject用于非父子组件之间共享数据:

  • 比如有一些深度嵌套的组件,子组件想要获取父组件的部分内容;
  • 在这种情况下,如果我们仍然将props沿着组件链逐级传递下去,就会非常的麻烦

对于这种情况下,我们可以使用 Provide 和 Inject :

  • 无论层级结构有多深,父组件都可以作为其所有子组件的依赖提供者;

  • 父组件有一个 provide 选项来提供数据;

  • 子组件有一个 inject 选项来开始使用这些数据;

在父组件中,我们可以通过provide提供数据,例如:

export default {
  provide: {
    message: 'Hello, World!'
  }
}

在子组件中,通过inject注入数据:

export default {
  inject: ['message'],
  created() {
    console.log(this.message)  // 输出"Hello, World!"
  }
}

总结:

  • provide数据传出对应的是一个对象inject接收祖先传递的数据对应的是一个数组
  • 通过provide和inject,我们可以将数据提供给组件树中的任何一个组件,而不仅仅是父子组件之间。
  • provide和inject在使用时的一个重要问题是数据不是响应式的,也就是说,当提供的数据发生变化时,子组件不会自动更新。
  • 可以简单的看作“long range props”,除了:
    • 父组件不需要知道哪些子组件使用它 provide 的 property;
    • 子组件不需要知道 inject 的 property 来自哪里

处理响应式数据:

让provide提供的数据变成响应式:

  • 使用响应式的一些API来完成这些功能,比如computed函数
  • 这个computed是vue3的新特性,这里以先直接使用,后续会专门讲解;

例如,在父组件中,我们提供了一个设置主题的方法和一个存储主题的值:

export default {
  data() {
    return {
      theme: 'light'
    }
  },
  provide() {
    return {
      setTheme: this.setTheme,
      themeState: computed(() => this.theme)
    }
  },
  methods: {
    setTheme(value) {
      this.theme = value
    }
  }
}

在上述代码中,我们使用computed将theme属性包装到themeState属性中,当theme属性变化时,themeState会自动更新并通知子组件,这样子组件就可以观察到themeState的变化并更新UI了。

然后在子组件中,我们通过inject注入themeState和setTheme方法,并使用themeState属性来渲染UI。

export default {
  inject: ['themeState', 'setTheme'],
  computed: {
    theme() {
      return this.themeState.value
    }
  }
}

需要注意的是,由于使用了computed,我们不能直接使用提供的数据,而是需要通过包装后的computed到value属性访问它们的值

总之,当使用provide和inject进行组件间通信时,为了确保数据的更新能够触发UI的更新,我们需要使用computed属性包装提供的数据。


(2)事件总线

Vue3从实例中移除了 o n 、 on、on、off 和 $once 方法,所以我们如果希望继续使用全局事件总线,要通过第三方的库:

  • Vue3官方有推荐一些库,例如 mitt 或 tiny-emitter, 这两个库虽然不再维护, 但还是可以使用的;
  • 这里我们使用coderwhy老师自己封装的hy-event-store 三方库

使用步骤:

  1. 安装这个库:npm install hy-event-store
  2. 封装一个工具eventbus.js:
import { HyEventBus } from "hy-event-store";

const eventBus = new HyEventBus()

export default eventBus

  1. 在项目中导入并引用:

在App.vue中监听事件;
在content.vue中触发事件;

content中触发事件:

<template>
  <div class="home-content">
    <button @click="btnClick">按钮</button>
  </div>
</template>

<script>
  import eventBus from './utils/event-bus'

  export default {
    methods: {
      btnClick() {
        console.log("myEvent事件被监听")
				// 发送事件到事件总线上
        eventBus.emit("myEvent", "sevgilid", 19, 1.86)
      }
    },
  }
</script>

App中监听事件:

<script>
  import eventBus from './utils/event-bus'
  import Home from './Home.vue'

  export default {
    components: {
      Home
    },
    created() {
			// 监听事件总线上的事件
      eventBus.on("myEvent", (name, age, height) => {
        console.log(name, age, height)
      })
    },
  }
</script>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux下一次复制cp多个文件(含scp命令)

linux cp 参数说明 -a&#xff1a;此选项通常在复制目录时使用&#xff0c;它保留链接、文件属性&#xff0c;并复制目录下的所有内容。其作用等于dpR参数组合。 -d&#xff1a;复制时保留链接。这里所说的链接相当于Windows系统中的快捷方式。 -f&#xff1a;覆盖已经存在的目…

实训笔记6.5

实训笔记 6.5一、座右铭二、上周回顾Java基本语法1、Java的安装和环境变量的配置2、Java的标识符、关键字和保留字3、Java中的变量和常量、字面量4、数据类型5、运算符6、流程控制7、数组8、JVM内存图9、面向对象的两大概念-类和对象 三、类的组成3.1 属性的声明和使用3.1.1 属…

扩散模型之DDPM

扩散模型之DDPM 文章前置生成模型总结 Design of DDPM正向加噪过程反向去噪过程 文章前置 最原文链接&#xff08;英文&#xff09;&#xff1a;What are Diffusion Models? 原文链接&#xff1a;Diffusion扩散模型大白话讲解&#xff0c;看完还不懂&#xff1f;不可能 原文链…

第10章 对象和类

第10章 对象和类 10.1 过程性编程和面相对象编程10.2 抽象和类10.2.1 类型是什么10.2.2 C中的类10.2.3 实现类成员函数10.2.4 使用类 10.1 过程性编程和面相对象编程 采用OOP方法时&#xff0c;首先从用户的角度考虑对象——描述对象所需的数据以及描述用户与数据交互所需的操…

【蓝桥刷题】备战国赛——异或三角

蓝桥杯2021国赛真题——异或三角 &#x1f680; 每日一题&#xff0c;冲刺国赛 &#x1f680; 题目导航&#xff1a; 异或三角 &#x1f387;思路&#xff1a;数位 d p dp dp d f s dfs dfs 思维 &#x1f531;思路分析&#xff1a; ✅数理基础&#xff1a; 按位异或&am…

(2017,AdaIN)用自适应实例归一化进行实时的任意风格迁移

Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization 公众号&#xff1a;EDPJ 目录 ​​​​​​​0. 摘要 1. 简介 2. 相关工作 3. 背景 3.1 批量归一化&#xff08;Batch Normalization&#xff0c;BN&#xff09; 3.2 实例归一化&#xff…

入驻京东直播间、成功借壳上市,交个朋友和时间“交朋友”

交个朋友一直走在“交朋友”的路上。 5月31日晚&#xff0c;交个朋友正式入驻京东直播间&#xff0c;首播销售额超1.5亿元&#xff0c;直播热度榜达人榜第一名&#xff0c;累计访问人次超1700万。据了解&#xff0c;京东618期间&#xff0c;除了日常直播外&#xff0c;交个朋友…

java并发编程:可见性、原子性、有序性三大特性详解

文章目录 可见性导致可见性的原因线程交叉执行重排序结合线程交叉执行共享变量更新后没有及时更新 如何解决可见性问题 原子性出现原子性问题的原因如何解决原子性问题 有序性导致有序性的原因如何解决有序性问题 总结 可见性 内存可见性&#xff0c;指的是线程之间的可见性&am…

IPv6NAT-PT实验:IPv4和IPv6地址转换的配置和验证

IPv6NAT-PT实验&#xff1a;IPv4和IPv6地址转换的配置和验证 【实验目的】 熟悉IPv6NAT-PT的概念。 掌握静态IPv6NAT-PT的配置 掌握动态IPv6NAT-PT的配置。 验证配置。 【实验拓扑】 设备参数如下表所示。 设备 接口 IP地址 子网掩码 默认网关 R1 S0/0 192.168.12…

2023HW护网红队必备工具总结

一、信息收集 1、AppInfoScanner 一款适用于以HVV行动/红队/渗透测试团队为场景的移动端(Android、iOS、WEB、H5、静态网站)信息收集扫描工具&#xff0c;可以帮助渗透测试工程师、红队成员快速收集到移动端或者静态WEB站点中关键的资产信息并提供基本的信息输出,如&#xff…

Java设计模式—模板方法模式

前言&#xff1a;模板方法模式是模板模式的一个具体实现&#xff0c;它定义了一个抽象类&#xff0c;其中包含一个模板方法和若干个基本方法。其中模板方法定义了算法骨架&#xff0c;而基本方法则由子类来实现。因此&#xff0c;模板方法在定义算法的结构方面提供了支持&#…

springMvc 解决 multipart/form-data 方式提交请求 不能获取非文件类型参数的问题和指定springmvc获取静态资源路径

问题&#xff1a; RequestMapping(value "/test",method RequestMethod.POST)ResponseBodypublic String test(String name,String id,MultipartFile file){System.out.println(name);System.out.println(id);System.out.println(file.getOriginalFilename());ret…

onnx模型的修改与调试demo

主要参考&#xff1a; 模型部署入门教程&#xff08;五&#xff09;&#xff1a;ONNX 模型的修改与调试 第五章&#xff1a;ONNX 模型的修改与调试 使用netron 可视化模型 读写onnx 构造onnx 创建一个描述线性函数 output axb 的onnx模型。 需要两个节点&#xff0c;第一个…

造轮子--自己封装一个 start

背景 都说不要造轮子&#xff0c;但是青柠认为&#xff0c;有的时候&#xff0c;造个轮子&#xff0c;更有助于清楚详细的业务逻辑&#xff0c;所以咱也自己写一个轮子&#xff0c;搞个系统开发。大体思路就是先搭建基础框架&#xff0c;然后细写业务逻辑&#xff0c;会涵盖主…

图解HTTP书籍学习2

确保Web安全的HTTPS HTTP的缺点 ●通信使用明文&#xff08;不加密&#xff09;&#xff0c;内容可能会被窃听 ●不验证通信方的身份&#xff0c;因此有可能遭遇伪装 ●无法证明报文的完整性&#xff0c;所以有可能已遭篡改 加密处理防止被窃听 通信的加密 一种方式就是…

【React】类组件,JSX语法,JSX原理,传递参数,条件渲染,列表渲染

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录 React使用组件&#xff08;类组件&#xff09;JSX语法书写规范JSX插入的内容JSX属性绑定JSX类绑…

前端基础(CSS)——css介绍 常用样式 案例—进化到Bootstrap——进化到Element-UI(未完待续)

目录 引出CSS相关1.css写在哪里&#xff1f;2.css的选择器【重要】&#xff08;1&#xff09;标签选择器---div{}&#xff08;2&#xff09;id选择器----#div01{}&#xff08;3&#xff09;类选择器---class"div01"&#xff0c;.dav01{}&#xff08;4&#xff09;后代…

saas产品私有化(一) 缓存中间件适配

一.背景 名词解释:私有化一般指的是在对客交付过程中,客户由于自身数据敏感,成本控制等原因要求交付乙方将售卖的服务利用现有甲方的硬件设备或者云服务进行服务的部署. 面向场景:一般特制的是saas化的云服务软件提供商的对特殊客群的场景.其中saas行业中比较起步和规模比较大的…

流氓软件篡改微软EDGE浏览器主页面的那些伎俩

微软的EDGE浏览器很好用&#xff0c;但也很容易被绑架&#xff0c;在网上下载各类免费软件&#xff0c;只要你安装完&#xff0c;十有八九就给你把主页改成某某导航了。尽管打开EDGE直接进入360、毒霸、好123等链接对上网影响也不大&#xff0c;打开这些导航页面后&#xff0c;…

IMX6ULL裸机篇之I2C实验主控代码说明二

一. I2C实验 I2C实验内容&#xff1a; 学习如何使用 I.MX6U 的 I2C 接口来驱动 AP3216C&#xff0c;读取 AP3216C 的传感器数据。 I2C读写数据时序图&#xff1a; I2C写数据时序图如下&#xff1a; I2C读数据时序图如下&#xff1a; 二. I2C主控读写时序 1. 读数据与写数…