Vue组件之间的通信

一、父子组件之间的通信

1.父传子

父组件传递数据给子组件通过props属性

Props是你可以在组件上注册一些自定义的attribute

父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值;

Props有两种常见的用法:字符串数组对象类型

(1)基本使用
  • 字符串数组
  • 对象类型:可以限制attribute的类型是否必须默认值

父组件:

<template>
  <div>
    <!-- 方式一:父组件注册一些自定义的attribute直接传数据,子组件通过props接受 -->
    <!-- <show-message title="hhaha" content="dfls"></show-message> -->
    <!-- 方式二:父组件注册一些自定义的attribute传递data中的数据,如数组、对象等类型,子组件通过props接受 -->
    <show-message :title="title" :content="content"></show-message>
  </div>
</template>

<script>
import ShowMessage from './ShowMessage.vue'
  export default {
    components: {
      ShowMessage
    },
    data() {
      return {
        title: "班底改变",
        content: {
          name: 'tjx',
          age: 19
        }
      }
    }
  }
</script>

子组件:

<template>
  <div>
    <h2>{{ title }}</h2>
    <h2>{{ content.name }}</h2>
    <h2>{{ content.age }}</h2>
  </div>
</template>

<script>
export default {
  // 数组和对象
  // 1.数组 只能告诉名称,直接通过名称使用
  // props: ["title", "content"]

  // 2.对象 可以指定名称以及类型等
  props: {
    title: String,
    content: {
      type: Object,
      // 必传
      // required: true,
      // 默认值 当type为Object时这里提示default要为function
      default() {
        return {
          name: "",
          age: 0,
        };
      },
    },
  },
};
</script>
(2)非Prop的Attribute

什么是非Prop的Attribute呢? 当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的 Attribute;常见的包括class、style、id属性等;

  • 当组件有单个根节点时,非Prop的Attribute将自动添加到根节点的Attribute中(attribute的继承);

在这里插入图片描述

2.子传父

子组件将数据传递给父组件通过**$emit**触发事件;

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

父组件:

<template>
  <div>
    <h2>当前计数:{{counter}}</h2>
    <!-- 父组件通过v-on(@)的方式传入要监听的事件名称,并绑定到对应的方式中去 -->
    <counter-operation @add="addOne" @sub="subOne" @addN="addNum"></counter-operation>
  </div>
</template>

<script>
import CounterOperation from "./CounterOperation.vue"
  export default {
    data() {
      return {
        counter: 0
      }
    },
    components: {
      CounterOperation
    },
    methods: {
      addOne() {
        this.counter++
      },
      subOne() {
        this.counter--
      },
      // 传递的参数n
      addNum(n) {
        this.counter += n
      }
    }
  }
</script>

**子组件:**内部其实是监听两个按钮的点击,点击之后通过 this.$emit的方式发出去事件

<template>
  <div>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
    <input type="text" v-model.number="num" />
    <button @click="incrementN">+n</button>
  </div>
</template>

<script>
export default {
  methods: {
    // 注册需要触发的时间
    // 1.数组写法 (定义事件名)
    // emits: ["add", "sub", "addN"],
    // 2.对象写法 (可以对传递的参数进行验证)
    // 定义事件名
    emits: {
      add: null,
      sub: null,
      addN: payload => {
        // 验证参数是否符合要求
        if (payload > 10) {
          // 为true时传递
          return true
        }
        return false
      }
    },
    // 子组件通过监听按钮的点击事件,然后通过$emit的方式将事件发送出去
    increment() {
      this.$emit("add");
    },
    decrement() {
      this.$emit("sub");
    },
    incrementN() {
      // 传递参数
      this.$emit("addN", this.num)
    },
  },
  data() {
    return {
      num: 0,
    };
  },
};
</script>

二、非父子组件之间的通信

1.Provide和Inject

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

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

爷组件:

<template>
  <div>
    <home></home>
    <button @click="addName">addName</button>
  </div>
</template>

<script>
import Home from "./Home.vue";
import {computed} from "vue"
export default {
  components: {
    Home,
  },
  // 基本使用
  // provide: {
  //   name: "tjx",
  //   age: 18
  // }
  // 写成一个函数就可以调用data中的数据
  provide() {
    return {
      name: "tjx",
      age: 18,
      // 如何让这里变成响应式的? 使用computed函数
      length: computed(() => this.names.length)
    };
  },
  data() {
    return {
      names: ["agc", "bsd"]
    }
  },
  methods: {
    addName() {
      this.names.push("tjxyn")
    }
  }
};
</script>

孙组件:

<template>
  <div>
    <h2>{{name}} {{age}} {{length}}</h2>
  </div>
</template>

<script>
  export default {
// 注入
    inject: ["name", "age", "length"]
  }
</script>

2.全局事件总线miit库

Vue3从实例中移除了 o n 、 on、 onoff 和 $once 方法,所以我们如果希望继续使用全局事件总线,要通过第三方的库: Vue3官方有推荐一些库,例如 mitt 或 tiny-emitter;

安装:npm install mitt

封装工具:eventbus.js

import mitt from "mitt"
const emitter = mitt();
export default emitter;
  • 在Home.vue中监听事件;
  • 在App.vue中触发事件;

在这里插入图片描述

Mitt的事件取消:

// 取消emitter中所有的监听
emitter.all.clear()

// 定义一个函数
function onFoo() {}
emitter.on('foo', onFoo) // 监听
emitter.off('foo', onFoo) // 取消监听

三、插槽

Vue中将 元素作为承载分发内容的出口;

在封装组件中,使用特殊的元素就可以为封装组件开启一个插槽;

该插槽插入什么内容取决于父组件如何使用;

1.基本使用

App.vue:

<template>
  <div>
    <!-- 基本使用: 直接插入内容 -->
    <my-slog-cpn>
      <button>我是按钮</button>
    </my-slog-cpn>

    <!-- 插入自己的组件 -->
    <my-slog-cpn>
      <my-button></my-button>
    </my-slog-cpn>

    <!-- 插槽默认值 -->
    <my-slog-cpn></my-slog-cpn>

    <!-- 插入很多内容 -->
    <my-slog-cpn>
      <!-- 所有内容都替换插槽 -->
      <h2>哈哈哈</h2>
      <button>按钮</button>
      <strong>strong</strong>
    </my-slog-cpn>
  </div>
</template>

<script>
import MySlogCpn from "./MySlotCpn.vue";
import MyButton from "./MyButton.vue";
export default {
  components: {
    MySlogCpn,
    MyButton,
  },
};
</script>

MySlogCpn.vue:

<template>
  <div>
    <h2>组件开始</h2>
    <slot>
      <i>默认值1</i>
    </slot>
    <slot>
      <i>默认值2</i>
    </slot>
    <h2>组件结束</h2>
  </div>
</template>

<script>
  export default {}
</script>

2.具名插槽

在这里插入图片描述

3.作用域插槽

有时候希望插槽可以访问到子组件中的内容是非常重要的: 当一个组件被用来渲染一个数组元素时,我们使用插槽,并且希望插槽中没有显示每项的内容;这个Vue给我们提供了作用域插槽

  • 基本使用

在这里插入图片描述

  • 果我们的插槽是默认插槽default,那么在使用的时候 v-slot:default="slotProps"可以简写为v-slot="slotProps"
  • 并且如果我们的插槽只有默认插槽时,组件的标签可以被当做插槽的模板来使用,这样,我们就可以将 v-slot 直 接用在组件上:
<show-names :names="names" v-slot="slotProps">
    	<span>{{slotProps.item}}-{{slotProps.index}}</span>
</show-names>
  • 默认插槽和具名插槽混合

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值