• Vue组件之间的通信-父传子-子传父


    Vue的组件嵌套

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

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

    我们来分析一下下面代码的嵌套逻辑,假如我们将所有的代码逻辑都放到一个App.vue组件中:

    • 我们会发现,将所有的代码逻辑全部放到一个组件中,代码是非常的臃肿和难以维护的。
    • 并且在真实开发中,我们会有更多的内容和代码逻辑,对于扩展性和可维护性来说都是非常差的。
    • 所以,在真实的开发中,我们会对组件进行拆分,拆分成一个个功能的小组件。
    <template>
      <div id="app">
        <div>
          <h2>Headerh2>
          <h2>NavBarh2>
        div>
        <div>
          <h2>Bannerh2>
        <ul>
          <li>商品列表1li>
          <li>商品列表2li>
          <li>商品列表3li>
          <li>商品列表4li>
          <li>商品列表5li>
        ul>
        div>
        <div>
          <h2>Footerh2>
          <h2>免责声明h2>
        div>
      div>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    我们可以按照如下的方式进行拆分
    在这里插入图片描述

    按照如上的拆分方式后,我们开发对应的逻辑只需要去对应的组件编写就可, App.vue中的代码如下 :

    <template>
      <div id="app">
    		<!-- 头部 -->
        <app-header></app-header>
    
    		<!-- 内容 -->
        <app-main></app-main>
    
    		<!-- 底部 -->
        <app-foot></app-foot>
        
      </div>
    </template>
    
    <script>
      // 引入子组件
      import AppHeader from './components/AppHeader.vue'
      import AppMain from './components/AppMain.vue'
      import AppFoot from './components/AppFoot.vue'
    
      export default {
    		// 注册组件
        components: {
          AppHeader,
          AppMain,
          AppFoot 
        }
      }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    上面的嵌套逻辑如下,它们存在如下关系

    • App组件是Header、Main、Footer组件的父组件
    • Main组件是Banner、ProductList组件的父组件

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

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

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


    Vue组件间通信

    父子组件之间如何进行通信呢?

    • 父组件传递给子组件:通过props属性
    • 子组件传递给父组件:通过$emit触发事件

    在这里插入图片描述

    1.父组件传递给子组件

    在开发中很常见的就是父子组件之间通信,比如父组件有一些数据,需要子组件来进行展示

    • 这个时候我们可以通过props来完成组件之间的通信

    什么是Props呢?

    • Props是你可以在组件上注册一些自定义的attribute
    • 父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值

    Props有两种常见的用法

    • 方式一:字符串数组, 数组中的字符串就是attribute的名称;
    • 方式二:对象类型,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、默认值等等;

    1.1数组的方式

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

    • 这时候就需要父组件通过自定义的attribute, 将数据传递给子组件
    <template>
      <div class="app">
    	
        <show-info name="chenyq" age="18" height="1.88" />
    
        
        <show-info name="kaisa" age="19" height="1.85" />
      div>
    template>
    
    <script>
      import ShowInfo from "./ShowInfo.vue"
    
      export default {
        components: {
          ShowInfo
        }
      }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 父组件通过自定义attribute将数据传递给子组件, 子组件可以通过props接收
    <template>
      <div class="infos">
        <h2>姓名: {{ name }}h2>
        <h2>年龄: {{ age }}h2>
        <h2>身高: {{ height }}h2>
      div>
    template>
    
    <script>
      export default {
        // props数组的方式接收父组件数据
        props: ["name", "age", "height"]
      }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 这样就完成了父传子的通信, 展示效果如图 :

    在这里插入图片描述


    1.2对象的方式

    数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制,接下来我们来看一下对象的写法是如何让我们的props变得更加完善的。

    在开发中, 我们使用更多的是对象的方法

    当使用对象语法的时候,我们可以对传入的内容限制更多

    • 比如通过type指定传入的attribute的类型
    • 比如通过require指定传入的attribute是否是必传的
    • 比如通过default指定没有传入时,attribute的默认值

    例如父组件中, 我们再展示一个信息, 这个信息不传递任何数据

    <template>
      <div class="app">
       
        <show-info name="chenyq" :age="18" :height="1.88" />
    
        
        <show-info name="kaisa" :age="19" :height="1.85" />
    
        
        <show-info />
      div>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    子组件中使用对象的方式接收父组件传递的数据

    <template>
      <div class="infos">
        <h2>姓名: {{ name }}h2>
        <h2>年龄: {{ age }}h2>
        <h2>身高: {{ height }}h2>
      div>
    template>
    
    <script>
      export default {
        // 2.props对象的方式接收父组件数据
        props: {
          name: {
            // 指定传入的类型
            type: String,
            // 指定是否必传项
            require: false,
            // 指定传入默认值
            default: "我是默认名字"
          },
          age: {
            type: Number,
            require: false,
            default: 10
          },
          height: {
            type: Number,
            require: false,
            default: 1.5
          }
        }
      }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    展示效果如下图, 如果父组件没有传递数据, 那么会使用默认数据

    在这里插入图片描述


    补充一 : type可以指定如下类型 :

    • String
    • Number
    • Boolean
    • Array
    • Object
    • Date
    • Function
    • Symbol

    补充二: 对象类型其他写法

    export default {
    	props: {
    		// 1.如果是一个对象类型, 必须通过函数返回一个对象
    		friend: {
    			type: Object,
    			default() {
    				return { name: "aaa", age: 18, height: 1.88 }
    			}
    		},
    		// 2.多个可能的类型
    		propA: {
    			type: [String, Number]
        }
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.子组件传递给父组件

    什么情况下子组件需要传递内容到父组件呢

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

    我们如何完成上面的操作呢?

    • 首先,我们需要在子组件中定义好在某些情况下触发的事件名称
    • 其次,在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中
    • 最后,在子组件中发生某个事件的时候,根据事件名称触发对应的事件

    例如我们做一个计数器:

    • 计数器可以+1, +5, +10, -1, -5, -10, 我们将加的功能抽离成一个组件, 将减的功能抽离一个组件
    • 当我们子组件发生了加或者减的事件, 如何让父组件监听到呢, 这时候就需要使用子组件发生自定义事件给父组件监听
    • 子组件事件触发之后, 通过 this.$emit的方式发出去事件;
    <template>
      <div class="add">
        <button @click="btnClick(1)">+1button>
        <button @click="btnClick(5)">+5button>
        <button @click="btnClick(10)">+10button>
      div>
    template>
    
    <script>
      export default {
        methods: {
          btnClick(count) {
            // 发送自定义事件
            // 第一个参数, 自定义的事件名, 第二个参数, 是要传递的参数
            this.$emit("add", count)
          }
        },
      }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    <template>
      <div class="sub">
        <button @click="btnClick(1)">-1button>
        <button @click="btnClick(5)">-5button>
        <button @click="btnClick(10)">-10button>
      div>
    template>
    
    <script>
      export default {
        methods: {
          btnClick(count) {
            // 发送自定义事件
            // 第一个参数, 发送的自定义事件名, 第二个参数, 发送的参数
            this.$emit("sub", count)
          }
        },
      }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 父组件监听子组件发出的自定义事件, 然后执行对应的操作
    <template>
      <div class="app">
        <h2>当前计数: {{ counter }}h2>
        
        <add-counter @add="addBtnClick" />
        
        <sub-counter @sub="subBtnClick" />
      div>
    template>
    
    <script>
      import AddCounter from "./AddCounter.vue"
      import SubCounter from "./SubCounter.vue"
    
      export default {
        components: {
          AddCounter,
          SubCounter
        },
        data() {
          return {
            counter: 0
          }
        },
        methods: {
          addBtnClick(count) {
            this.counter += count
          },
          subBtnClick(count) {
            this.counter -= count
          }
        },
      }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在项目开发中, 有可能子组件发送自定义事件的开发者, 和监听自定义事件的开发者不是同一个人 :

    • 为了方便团队合作, Vue三提供了一个emits API, 当其他人想知道你发送了什么事件, 便可以在emits中一目了然
    export default {
    	emits: ["sub", "add"]
    }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    响应式对象添加 响应式属性的三种方式;响应式对象添加属性后页面不更新
    K8s复习笔记12--Ingress/Egress实验9则
    后端工程师之路(8)-springboot
    浏览器缓存 四种缓存分类 两种缓存类型
    企业微信如何创建微盘共享空间?
    C++程序设计-练手题集合【期末复习|考研复习】
    MySQL 允许SQL最大长度
    代码随想录算法训练营第六十五天 | 岛屿数量 深搜、岛屿数量 广搜、岛屿的最大面积
    flutter_gen依赖
    Python史上最全种类数据库操作方法,你能想到的数据库类型都在里面!甚至还有云数据库!
  • 原文地址:https://blog.csdn.net/m0_71485750/article/details/125873101