VUE2.0组件间7中通信方式

VUE2.0组件间通信

Vue组件通信的方式,这是在面试中一个非常高频的问题,很多人只知道props和 $emit,下面简单介绍一下Vue的一些其他传值方式。


一、props/$emit

props/$emit可以说Vue里面最常见的通信方式。
props: props用于接收来自父组件通过v-bind传递的数据,可以通过type、default、required、validator等配置来设置属性的类型、默认值、是否必传和校验规则。

$emit: 在组件通信时,我们通常使用 $emit来触发父组件v-on在子组件上绑定相应事件的监听。

`自定义 v-model, 父组件 通过 v-bind: value, 子组件通过 this.$emit('input', newValue) 和  this.$emit('change', newValue)`
代码示例

父向子传值:父组件通过 :type="type" :size="size" 将父组件type/size 值传递给子组件。
子向父传值:父组件通过@click="handleClick"在子组件上绑定了 click 事件的监听,子组件 my-button标签点击时,会触发 click 回调函数, 通过this.$emit(click', message) 将子组件 message 的值赋值给父组件。


<template>
  <!-- 这是子组件 -->
  <button
  	 class="my-button" 
  	 :class="['my-button-size-'+size, 'my-button-'+type, ]" 
  	 @click="click">
      <slot></slot>
  </button>
</template>

<script>
export default {
  name: 'MyButton',
  props: {
    // size 属性是用来控制按钮的大小样式,通过不同 size 设置不同的类名
    size: {
      type: String,
      default: 'big'
    },
    // size 属性是用来控制按钮的颜色样式,type来控制主题、颜色
    type: {
      type: String,
      default: 'default'
    }
  },
  methods: {
    click() {
      this.$emit('click', `子组件点击了一下按钮`)
    }
  }
}
</script>
<template>
  <!-- 这是父组件 -->
  <div>
    <my-button
    	:type="type"
    	:size="size"
    	@click="handleClick">按钮</my-button>
    <p>子组件:{{ childCallbackWord }}</p>
  </div>
</template>
<script>
export default {
  name: 'parent-component',
  data () {
    return {
      type: 'primary',
      size: 'small',
      childCallbackWord: '' // 子组件点击事件回调参数
    }
  },
  methods: {
    handleClick (msg) {
      // 接受子组件的信息,并将其赋值给childCallbackWord
      this.childCallbackWord = msg
    }
  }
}
</script>

在这里插入图片描述

二、v-slot

v-slot 是 Vue2.6 版本中新增的用于统一实现插槽和具名插槽的api。

v-slot 在 template 标签中用于提供具名插槽或需要接收 prop 的插槽,如果不指定 v-slot ,则取默认值 default 。

代码示例

父向子传值:父组件通过默认slot 将父组件的message值传递给子组件,子组件通过<slot></slot>接收到相应内容,实现了父向子传值。

  <!-- 子组件参照props/$emit中示例的子组件  -->
<template>
  <!-- 这是父组件 -->
  <div class="parent">
    <p><input type="text" v-model="message"></p>
    <my-button :type="type" size="small">{{message}}</my-button>
  </div>
</template>
<script>
export default {
  name: 'example-button',
  data () {
    return {
      type: 'primary',
      message: '' // 插槽message
    }
  }
}
</script>

在这里插入图片描述

三、$refs/ $parent/ $children/ $root

我们也同样可以通过 $refs/$parent/$children/$root 等方式获取 Vue 组件实例,得到实例上绑定的属性及方法等,来实现组件之间的通信。

$refs: 我们通常会将 $refs绑定在DOM元素上,来获取DOM元素的 attributes。在实现组件通信上,我们也可以将 $refs 绑定在子组件上,从而获取子组件实例。

$parent: 我们可以在 Vue 中直接通过this.$parent来获取当前组件的父组件实例(如果有的话)。

$children: 同理,我们也可以在 Vue 中直接通过this.$children来获取当前组件的子组件实例的数组。但是需要注意的是,this.$children数组中的元素下标并不一定对用父组件引用的子组件的顺序,例如有异步加载的子组件,可能影响其在 children 数组中的顺序。所以使用时需要根据一定的条件例如子组件的name去找到相应的子组件。

$root: 获取当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。通过 $root ,我们可以实现组件之间的跨级通信。

代码示例

以上四个都是获取对应的示例,用法基本相同,以下以$parent $children为例。

父向子传值:子组件通过$parent.message获取到父组件中message的值。

子向父传值:父组件通过 $children 获取子组件实例的数组,遍历通过 child。$options.name 获取对应name的child实例。再通过child实例 child.message获取到子组件中message的值。

<template>
      <!-- 这是子组件 -->
      <p>收到来自父组件的消息:{{ $parent.message }}</p>
</template>
<script>
export default {
  name: 'my-button',
  data () {
    return {
      message: '我是子组件中的message'
    }
  }
}
</script>
<template>
 <!-- 这是父组件 -->
  <div class="parent">
    <my-button></my-button>
    <p>子组件中message:{{ child.message }}</p>
  </div>
</template>
<script>
export default {
  name: 'parent',
  data () {
    return {
      child: {},
      message: '我是父组件中的message' 
    }
  },
  mounted () {
    this.child = this.$children.find((child) => {
      // 通过options.name获取对应name的child实例
      return child.$options.name === 'my-button' 
    })
  }
}
</script>

在这里插入图片描述

四、 $attrs / $listener

$attrs: 用来接收父作用域中不作为 prop 被识别的 attribute 属性,并且可以通过v-bind="$attrs"传入内部组件——在创建高级别的组件时非常有用。

$listeners: 包含了父作用域中的 v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用,这里在传递时的使用方法和 $attrs 十分类似。

// 3级组件C
<template>
  <div class="compc">
    <!--A组件keyup的监听回调绑在该input上-->
    <input
    	name="compC"
   	 	type="text" 
   	 	v-model="message" 
   	 	v-on="$listeners" /> 
    <p>收到来自A组件的消息:{{ $attrs.messageFromA }}</p>
  </div>
</template>
<script>
export default {
  name: 'Compc',
  data() {
    return {
      message: '',
    }
  },
}
</script>
// 2级组件B
<template>
  <div class="compb">
    <h4>this is B component</h4>
    <!--A组件keyup的监听回调绑在该input上-->
    <input
    	 name="compB"
    	 type="text" 
    	 v-model="message"
    	 v-on="$listeners" />  
    <p>收到来自A组件的消息:{{ $attrs.messageFromA }}</p>
    <!--A组件keyup的监听回调、A组件传递的attrs继续传递给C组件-->
    <CompC v-bind="$attrs" v-on="$listeners" /> 
  </div>
</template>
<script>
import CompC from './compC'
export default {
  name: 'CompB',
  components: {
    CompC,
  },
  data() {
    return {
      message: '',
    }
  },
}
</script>
// A组件
<template>
  <div class="compa">
    <h3>this is A component</h3>
    <input type="text" v-model="message" />
    <p>收到来自{{ comp }}的消息:{{ messageFromComp }}</p>
    <!--监听子孙组件的keyup事件,将message传递给子孙组件-->
    <CompB :messageFromA="message" @keyup="receive" />  
  </div>
</template>
<script>
import CompB from './compB'
export default {
  name: 'CompA',
  data() {
    return {
      message: '',
      messageFromComp: '',
      comp: '',
    }
  },
  components: {
    CompB,
  },
  methods: {
    receive(e) { 
    // 监听子孙组件keyup事件的回调,
    // 并将keyup所在input输入框的值赋值给messageFromComp
      this.comp = e.target.name
      this.messageFromComp = e.target.value
    },
  },
}
</script>

在这里插入图片描述

五、provide/inject

provide inject 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深

provide: 是一个对象,或者是一个返回对象的函数。该对象包含可注入其子孙的 property ,即要传递给子孙的属性和属性值。
提示:由于vue provide 和 inject 绑定并不是可响应的,所以要用object类型而不是string/number类型

injcet: 一个字符串数组,或者是一个对象。当其为字符串数组时,使用方式和props十分相似,只不过接收的属性由data变成了provide中的属性。当其为对象时,也和props类似,可以通过配置default和from等属性来设置默认值,在子组件中使用新的命名属性等。

代码示例
// C组件
<template>
 <div class="compc">
  <p>收到来自A组件的消息:{{ messageFromA && messageFromA.content }}</p>
 </div>
</template>
<script>
export default {
  name: 'CompC',
  // 通过inject接受A中provide传递过来的message
  inject: ['messageFromA']
}
</script>
// B组件
<template>
 <div class="compB" >
  <p>收到来自A组件的消息:{{ messageFromA && messageFromA.content }}</p>
  <ComC />
 </div>
</template>
<script>
import ComC from './C.vue'
export default {
  name: 'ComB',
  components: {
    ComC
  },
  // 通过inject接受A中provide传递过来的message
  inject: ['messageFromA'] 
}
</script>
<template>
  <div class="compA">
    <h3>这是A组件</h3>
    <ComB />
  </div>
</template>
<script>
import ComB from './B.vue'
export default {
  name: 'CompA',
  components: {
    ComB
  },
  provide () {
    return {
      // 将message通过provide传递给子孙组件
      messageFromA: {
        content: '这是A的message'
      }
    }
  }
}
</script>

在这里插入图片描述

六、eventBus

eventBus又称事件总线,通过注册一个新的Vue实例,通过调用这个实例的$emit$on等来监听和触发这个实例的事件,通过传入参数从而实现组件的全局通信。它是一个不具备 DOM 的组件,有的仅仅只是它实例方法而已,因此非常的轻便。

// bus.js,使用时通过import引入
import Vue from 'vue'
export const Bus = new Vue()
/*
vm.$on( event, callback )
参数:
{string | Array<string>} event (数组只在 2.2.0+ 中支持)
{Function} callback
用法:
监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。

vm.$once( event, callback )
参数:
{string} event
{Function} callback
用法:
监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。

vm.$off( [event, callback] )
参数:
{string | Array<string>} event (只在 2.2.2+ 支持数组)
{Function} [callback]
用法:
移除自定义事件监听器。
如果没有提供参数,则移除所有的事件监听器;
如果只提供了事件,则移除该事件所有的监听器;
如果同时提供了事件与回调,则只移除这个回调的监听器。

vm.$emit( eventName, […args] )
参数:
{string} eventName
[...args]
触发当前实例上的事件。附加参数都会传给监听器回调。
代码示例
<template>
    <div>
        <p>我是父组件: {{ message }}</p>
        <Son></Son>
    </div>
</template>

<script>
import Son from './Son.vue'
import { Bus } from '../bus.js'
export default {
  data () {
    return {
      message: ''
    }
  },
  components: {
    Son
  },
  mounted () {
    // 监听grandson事件
    Bus.$on('grandson', (msg) => {
      this.message = msg
    })
  },
  methods: {
    handleMmsg (msg) {
     //移除事件监听,故Parent只监听一次,减少内存消耗
      Bus.$off('grandson')
    }
  }
}
</script>
<template>
    <div>
        <p>我是子组件 : {{  message }}</p>
        <Grandson></Grandson>
    </div>
</template>

<script>
import Grandson from './Grandson.vue'
import { Bus } from '../bus.js'
export default {
  name: 'my-button',
  components: {
    Grandson
  },
  data () {
    return {
      message: ''
    }
  },
  mounted () {
    // 监听事件
    Bus.$on('grandson', (msg) => {
      this.message = msg
    })
  }
}
</script>
<template>
    <div>
        <p>我是孙子组件 </p>
        <button @click="handleMsg">点击发出消息</button>
    </div>
</template>
<script>
import { Bus } from '../bus.js'
export default {
  methods: {
    handleMsg () {
      Bus.$emit('grandson', '我是组件GrandSon')
    }
  }
}
</script>

在这里插入图片描述
实现了全局通信:即父子组件相互通信、兄弟组件相互通信、跨级组件相互通信。孙组件Bus.$emit('grandson', message) 将message作为参数传入;同时通过this.$bus.$on('grandson', message)监听其他组件的grandson事件,获取实例当前组件示例message。这样其他组件都能接收到相应的信息,实现全局通信。

七、Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
具体参照 Vuex官方文档[1]

代码示例

具体的代码就不放出来了…

总结

以上提及到7总组件间通信的方式,可能没有涉及全,但希望能帮助到你们!
在这里插入图片描述
props/$emit:可以实现父子组件的双向通信,在日常的父子组件通信中一般会作为我们的最常用选择。

v-slot:可以实现父子组件单向通信(父向子传值),在实现可复用组件,向组件中传入DOM节点、html等内容以及某些组件库的表格值二次处理等情况时,可以优先考虑v-slot。

$refs/$parent/$children/$root:可以实现父子组件双向通信,其中 $root可以实现根组件实例向子孙组件跨级单向传值。在父组件没有传递值或通过v-on绑定监听时,父子间想要获取彼此的属性或方法可以考虑使用这些api。

$attrs/$listeners:能够实现跨级双向通信,能够让你简单的获取传入的属性和绑定的监听,并且方便地向下级子组件传递,在构建高级组件时十分好用。

provide/inject:可以实现跨级单向通信,轻量地向子孙组件注入依赖,这是你在实现高级组件、创建组件库时的不二之选。

eventBus:可以实现全局通信,在项目规模不大的情况下,可以利用eventBus实现全局的事件监听。但是eventBus要慎用,避免全局污染和内存泄漏等情况。

Vuex:可以实现全局通信,是vue项目全局状态管理的最佳实践。在项目比较庞大,想要集中式管理全局组件状态时,那么安装Vuex准没错!

参考资料

[1] Vuex官方文档: https://vuex.vuejs.org/zh/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值