常用的5种Vue组件通信方法

组件之间传值、调用函数的方法

方法使用场景
props/$emit父向子传递数据是通过 props(只是传值),子向父是通过$emit给父函数传参并触发
$refs父给子的数据赋值或触发子的函数方法,可传值也可以调用函数
$emit/$on可实现任何组件间的通信,包括父子、兄弟、跨级
provide/inject适用于多层嵌套的组件之间,一个祖先组件向其所有子孙后代注入依赖,在起上下游关系成立的时间里始终生效,但不是很推荐这种方法
$attrs/$listeners适用于跨级推荐,仅传递数据

总结:前面三种方法较为常用,可以解决绝大部分需求,后面两种不太常用。

VUE组件间的各类关系

​在 Vue 中,组件之间可以有不同的关系,包括父子关系、兄弟关系和祖先后代关系。这些关系可以通过组件的嵌套和组件之间的通信来建立和维护。下面是一些常见的组件关系:

父子关系(Parent-Child Relationship):一个组件可以包含另一个组件,被包含的组件称为子组件,包含子组件的组件称为父组件。父组件可以通过 props 向子组件传递数据,子组件可以通过事件向父组件发送消息。

兄弟关系(Sibling Relationship):同一个父组件下的多个子组件之间可以是兄弟关系。兄弟组件之间可以通过共享父组件传递的数据或事件来通信。

祖先后代关系(Ancestor-Descendant Relationship):一个组件可以作为另一个组件的祖先组件或后代组件。祖先组件是指包含当前组件的所有嵌套层次上的组件,后代组件是指当前组件的所有嵌套层次下的组件。祖先组件和后代组件之间的通信可以通过 props 和事件进行。![组件关系](https://img-blog.csdnimg.cn/direct/2654e5183e3c4063a570a3649d6ff759.jpeg#pic_center)

组件关系

props/$emit

父组件向子组件传值props

props有数组和对象的两种写法:

#数组,简单的用法
props:['parentValue']
#对象,更高级的用法,有多种配置项,下面只列举了常用的三个
props:{
        parentValue:{
                  // 多个可能的类型
                  type: [String, Number],
                  required: true,
                  // 自定义验证函数
                  validator: (value) => {
                      return parseInt(value) >= 0
                  }
      }

在父组件 A.vue:

<template>
  <div>
    <h1>父组件 A</h1>
    //使用v-on绑定要传的值parentdata,message是子组件要接受的变量名
    <Child-component :message="parentdata"></Child-component>
  </div>
</template>
<script>
//导入子组件
import ChildComponent from './B.vue';

export default {
  name: 'ParentComponent',
  //定义子组件
  components: {
    ChildComponent,
  },
  data() {
    return {
      //定义值parentdata
      parentdata: 'Hello from Parent',
    };
  },
};
</script>

在子组件B.vue中:

<template>
  <div>
    <h2>子组件 B</h2>
    <p>{{ receivedData }}</p>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  //接收值
  props: {
  //接受方法
    message: {
      type: String,
      required: true,
    },
  },
  data(){
	  return{
	  		user_defined:this.message
	  }
  }
  computed: {
    receivedData() {
      return this.message;
    },
  },
};
</script>

组件中的数据共有三种形式:data、props、computed
值得注意的是,props的传值是单向的:当父组件改变props的值,子组件使用props接收的值也会发生改变(用data去接受不会改变,用computed接收会改变,具体原理请自行思考),而子组件的值发生改变,是不会影响父组件的值的。

子组件触发父组件的方法$emit

要在子组件 B.vue 中使用 $emit 方法触发父组件 A.vue 的事件,可以按照以下步骤进行操作:
在父组件 A.vue:

<template>
  <div>
    <h1>父组件 A</h1>
    <child-component @customEvent="handleCustomEvent"></child-component>
  </div>
</template>

<script>
import ChildComponent from './B.vue';

export default {
  name: 'ParentComponent',
  components: {
    ChildComponent,
  },
  methods: {
    handleCustomEvent(payload) {
      console.log('Received payload from child:', payload);
      // 处理从子组件接收到的数据
    },
  },
};
</script>

在子组件 B.vue:

<template>
  <div>
    <h2>子组件 B</h2>
    <button @click="triggerCustomEvent">触发事件</button>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  methods: {
    triggerCustomEvent() {
      const payload = 'Hello from Child';
      this.$emit('customEvent', payload);
    },
  },
};
</script>

在上述示例中,子组件 B.vue 中的按钮绑定了 @click 事件,并在事件处理程序中使用 this.$emit 方法触发了名为 customEvent 的自定义事件,并传递了一个 payload(有效载荷)作为参数。

在父组件 A.vue 中,使用 @customEvent 监听子组件触发的 customEvent 事件,并在相应的事件处理程序 handleCustomEvent 中接收传递的 payload 数据。

当子组件 B.vue 的按钮被点击时,triggerCustomEvent 方法将被调用,触发 customEvent 事件并传递 payload 数据给父组件 A.vue。父组件 A.vue 中的事件处理程序 handleCustomEvent 将接收到该 payload 数据,并进行相应的处理。

这样,通过 $emit 方法和自定义事件,可以在子组件中触发事件,并将数据传递给父组件进行处理。

$refs

父组件A使用 $refs 来向子组件 B.vue 传递值和调用方法时,可以按照以下步骤进行操作:
在父组件 A.vue:

<template>
  <div>
    <h1>父组件 A</h1>
    <child-component ref="childRef"></child-component>
    <button @click="passValueAndCallMethod">传递值和调用方法</button>
  </div>
</template>

<script>
import ChildComponent from './B.vue';

export default {
  name: 'ParentComponent',
  components: {
    ChildComponent,
  },
  methods: {
    passValueAndCallMethod() {
      const valueToPass = 'Hello from Parent';
      const childComponent = this.$refs.childRef;

      // 传递值
      childComponent.valueFromParent = valueToPass;

      // 调用方法
      childComponent.childMethod();
    },
  },
};
</script>

在子组件 B.vue:

<template>
  <div>
    <h2>子组件 B</h2>
    <p>{{ valueFromParent }}</p>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  data() {
    return {
      valueFromParent: '',
    };
  },
  methods: {
    childMethod() {
      console.log('Child method called');
      // 子组件方法的实现
    },
  },
};
</script>

在上述示例中,父组件 A.vue 中使用 ref=“childRef” 将子组件 B.vue 标记为 childRef 的引用。

在父组件 A.vue 的 passValueAndCallMethod 方法中,我们可以通过 this.$refs.childRef 获取到子组件 B.vue 的实例,并直接访问子组件的属性和方法。

通过 childComponent.valueFromParent = valueToPass,我们将值 valueToPass 从父组件传递给子组件的 valueFromParent 属性。

通过 childComponent.childMethod(),我们调用子组件的 childMethod 方法。

这样,通过 $refs,我们可以在父组件中获取到子组件的实例,并直接操作子组件的属性和方法,实现数据传递和方法调用的功能。

需要注意的是,当使用 $refs 时,确保在子组件被渲染后才能访问 $refs,因此最好在适当的生命周期钩子函数(如 mounted)或在合适的时机访问 $refs。

$emit/$on

刚刚我们的例子只是父传子,子传父进行传数据,当遇到组件嵌套比较深或比较复杂的情况(有递归组件时),我们可以使用事件总线 (EventBus) 。
在我的另一篇博客里开发可无限回复的评论区(评论的递归组件)用到了事件总线。
首先,创建一个新的 Vue 实例作为事件总线,在主文件(通常是 bus.js)中:

// bus.js
import Vue from 'vue';

// 创建事件总线实例
export const eventBus = new Vue();

然后,假设我们有三个组件 A.vue、B.vue 和 C.vue,其中 B.vue 嵌套在 A.vue 中,而 C.vue 嵌套在 B.vue 中。我们想要在 C.vue 中向 A.vue 传递值。可以按照以下步骤进行操作:
在 A.vue 中:

<template>
  <div>
    <h1>父组件 A</h1>
    <B-component></B-component>
  </div>
</template>

<script>
import BComponent from './B.vue';
import { eventBus } from './bus.js';

export default {
  name: 'AComponent',
  components: {
    BComponent,
  },
  mounted() {
    // 监听来自 C.vue 的事件
    eventBus.$on('valueFromC', this.handleValueFromC);
  },
  methods: {
    handleValueFromC(value) {
      console.log('Received value from C:', value);
      // 处理从 C.vue 接收到的数据
    },
  },
};
</script>

在 B.vue 中:

<template>
  <div>
    <h2>子组件 B</h2>
    <C-component></C-component>
  </div>
</template>

<script>
import CComponent from './C.vue';

export default {
  name: 'BComponent',
  components: {
    CComponent,
  },
};
</script>

在 C.vue 中:

<template>
  <div>
    <h3>子组件 C</h3>
    <button @click="passValueToA">向 A 传递值</button>
  </div>
</template>

<script>
import { eventBus } from './bus.js';

export default {
  name: 'CComponent',
  methods: {
    passValueToA() {
      const valueToPass = 'Hello from C';
      // 通过事件总线向父组件 A 传递值
      eventBus.$emit('valueFromC', valueToPass);
    },
  },
};
</script>

在上面的例子中中,我们在 bus.js 中创建了事件总线实例 eventBus。

在组件 A.vue 中,通过 eventBus.$on(‘valueFromC’, this.handleValueFromC) 监听来自 C.vue 的事件,并在 handleValueFromC 方法中处理接收到的数据。

在组件 C.vue 中,当按钮被点击时,通过 eventBus.$emit(‘valueFromC’, valueToPass) 向事件总线发送名为 valueFromC 的事件,并传递值 valueToPass。

这样,通过事件总线,组件 C.vue 可以向组件 A.vue 传递值,而组件 A.vue 可以监听和处理来自 C.vue 的数据。

使用事件总线可以方便地在组件之间传递数据,但在大型应用中可能会导致事件的管理变得困难。

provide/inject

使用 provide 和 inject 是另一种在组件之间传递值的方式。我们在父组件中提供provide数据,然后在子组件中注入inject并使用这些数据。需要注意的是,provide 和 inject 并不是响应式的,这意味着如果提供的值发生变化,子组件不会自动更新。所以基本上很少用到,下面是一个使用 provide 和 inject 进行组件传值的例子:

在父组件 A.vue 中:

<template>
  <div>
    <h1>父组件 A</h1>
    <child-component></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  name: 'AComponent',
  components: {
    ChildComponent,
  },
  provide: {
    valueFromParent: 'Hello from Parent',
  },
};
</script>

在子组件 ChildComponent.vue 中:

<template>
  <div>
    <h2>子组件</h2>
    <p>{{ injectedValue }}</p>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  inject: ['valueFromParent'],
  computed: {
    injectedValue() {
      return this.valueFromParent;
    },
  },
};
</script>

在上述示例中,父组件 A.vue 使用 provide 选项提供了名为 valueFromParent 的数据,并将其设置为 ‘Hello from Parent’。

在子组件 ChildComponent.vue 中,使用 inject 选项来注入父组件提供的数据。通过在 inject 数组中指定 ‘valueFromParent’,子组件可以访问父组件提供的 valueFromParent 数据。

在子组件中,我们使用 computed 属性来创建一个名为 injectedValue 的计算属性,它返回从父组件注入的值 valueFromParent。

这样,通过使用 provide 和 inject,父组件可以提供数据,而子组件可以通过注入并访问该数据。

再次强调,provide 和 inject 并不是响应式的,这意味着如果提供的值发生变化,子组件不会自动更新。

$attrs/$listeners

a t t r s 用于传递属性, attrs 用于传递属性, attrs用于传递属性,listeners 用于传递事件。下面是一个使用 $attrs 和 $listeners 进行组件传值的例子:
在父组件 A.vue 中:

<template>
  <div>
    <h1>父组件 A</h1>
    <child-component v-bind="$attrs" v-on="$listeners"></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  name: 'AComponent',
  components: {
    ChildComponent,
  },
};
</script>

在子组件 ChildComponent.vue 中:

<template>
  <div>
    <h2>子组件</h2>
    <p>{{ valueFromParent }}</p>
    <button @click="$emit('customEvent')">触发自定义事件</button>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  props: {
    valueFromParent: String,
  },
};
</script>

在上述示例中,父组件 A.vue 使用 v-bind=“$attrs” 将父组件的所有属性传递给子组件。这样,子组件可以接收到来自父组件的属性,并在自身的 props 中声明对应的属性。

父组件 A.vue 同样使用 v-on=“$listeners” 将父组件的所有事件监听器传递给子组件。这样,子组件可以接收到来自父组件的事件,并在自身触发相应的事件。

在子组件 ChildComponent.vue 中,我们声明了一个名为 valueFromParent 的 prop,用于接收来自父组件的值。

子组件中的按钮通过 $emit 方法触发了一个自定义事件 ‘customEvent’。这个事件会被传递给父组件,并在父组件的位置上执行相应的事件处理方法。

通过使用 $attrs 和 $listeners,我们可以将父组件的属性和事件传递给子组件,实现组件之间的值传递和事件触发。

需要注意的是,$attrs 和 $listeners 只会传递父组件中没有被子组件声明为 prop 的属性和事件监听器。$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。

总结

这些通信方法各有优劣,适用于不同的场景和需求。例如,Props 和 Emit 是常用的父子组件通信方式,适用于简单的数据传递,且传递是单向和可响应式的。Refs 可以在需要直接访问子组件或 DOM 元素时使用。Event Bus 适用于较为复杂的组件之间的通信。$attrs 和 $listeners 适用于将父组件的属性和事件传递给子组件,而不需要显式声明。Provide 和 Inject 可以在多层级嵌套的组件中传递数据但传递是不可响应式的。

方法总结
props 和 $emit使用 props 属性将数据从父组件传递到子组件。使用 emit 方法触发自定义事件,从子组件向父组件传递数据。
$refs使用 ref 属性给子组件或 DOM 元素添加引用。 通过 $refs 对象访问子组件或 DOM 元素的实例或属性。
Event Bus(事件总线)创建一个新的 Vue 实例作为事件总线。使用 $emit 方法触发事件,并使用 $on 方法监听事件,以实现组件之间的通信。
Provide 和 Inject使用 provide 选项在父组件中提供数据。使用 inject 选项在子组件中注入并使用父组件提供的数据。
$attrs 和 $listeners使用 v-bind=“ a t t r s " 将父组件的属性传递给子组件。使用 v − o n = " attrs" 将父组件的属性传递给子组件。使用 v-on=" attrs"将父组件的属性传递给子组件。使用von="listeners” 将父组件的事件监听器传递给子组件

如果有错误,欢迎各位大佬指出!!!

  • 55
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值