相关文章推荐
腼腆的香槟  ·  Typescript: Add ...·  1 年前    · 

之前简单做了一次vue通信方法的培训,在此记录一下培训的内容。

关于vue通信,大家最先想到的方法应该是props、ref、$emit、$parent,还有vuex,因为这也是我在项目中最常用到的方法,其实,还有其他的一些通信方法,下面我来作个简单的介绍:

我主要通过组件类型来介绍:

一、父子组件通信:

(1)父向子传值:

props: 一般用于父组件向子组件进行单向通信

ref: 为子组件指定一个索引名称,通过索引来操作子组件,用this.$refs.name 获取到组件实例,可以使用组件的所有方法

$children: 父组件也可以通过this.$children 访问它所有的子组件( 需要注意 $children 并不保证顺序 )

(2)子向父传值 :

$emit: 触发父组件的自定义事件 vm.$emit( event, arg )

$parent: 通过this.$parent可以直接访问该组件的父实例或组件(得到最近一级的父组件)

父子组件通信实例:

//父组件
<
template> <div class="box"> <a-button @click="onClick">传参给子组件</a-button> 我是父组件,点击次数:{{ msg }} <child ref="childRef" :msg="msg" @child-event="watchChildEvent"></child> </div> </div> </template> <script> import Child from "./son"; export default { components: { Child }, data() { return { msg: 0 methods: { onClick() { ++this.msg; //监听由子组件触发的事件,参数为子组件传来的数据 watchChildEvent(val) { this.msg = val; //监听由子组件触发的事件,参数为子组件传来的数据 watchParEvent(val) { this.parMsg = val; mounted() { //父组件调用子组件方法 this.$refs.childRef.getMessage("我是子组件一"); </script>
//子组件
<
template> <div class="son-box"> <a-button @click="targetClick">通过$emit传参到父组件</a-button><br /> <a-button @click="parClick">2、通过$parent传参到父组件</a-button><br /> 我是子组件1,点击次数:{{ msg }}<br /> {{ val }} </div> </template> <script> export default { components: {}, props: { msg: { type: [String, Number], //类型 default: 0 //可以设置默认值 data() { return { val: "" methods: { //由子组件的事件触发 自定义事件wathChildEvent targetClick() { this.$emit("child-event", 20); //参数改为this.msg ?? //使用$parent访问父组件(得到最近一级的父组件) parClick() { this.$parent.watchParEvent("2、我是使用$parent更改的父级msg"); //由父组件的事件触发 getMessage(m) { console.log(m); mounted() {} </script>

二、兄弟组件通信:

不使用 props 和 Vuex 时,可以用什么呢?

eventBus (中央事件总线event Bus),它允许两个子组件之间直接通讯,而不需要涉及父组件。是实现非父子组件通信的一种解决方案。但是当项目较大较复杂时,并不适合。到那时,vuex才是vue给我们提供的最理想的方式。 原理:就是创建一个vue实例,通过一个空的vue实例作为桥梁实现vue组件间的通信。 (在Vue2中可谓是最好用的非父子组件之间的通讯手段,但是Vue3宣布取消了,官方推荐的做法是让用户自己寻找使用第三方库,后续有时间会分享一篇vue3如何去实现event Bus)

eventBus 的用法如下:

第一步:项目中创建一个js文件(命名为event-bus.js),引入vue,创建一个vue实例,导出这个实例,代码如下(一共就两行):

 import Vue from 'Vue'
 export default new Vue

第二步:在两个需要通信的两个组件中分别引入这个event-bus.js

 import Bus from '这里是你引入event-bus.js的路径' // Bus可自由更换喜欢的名字 

第三步:传递数据的组件里通过vue实例方法$emit发送事件名称和需要传递的数据。(发送数据组件)

Bus.$emit('click',data) // 这个click是一个自定义的事件名称,data就是你要传递的数据。 

第四步:被传递数据的组件内通过vue实例方法$on监听到事件和接受到数据。(接收数据的组件)这里通常挂载监听在vue生命周期created和mounted当中的一个,具体使用场景需要具体的分析,这里不说这个。

Bus.$on('click',target => {
  console.log(target) //注意:发送和监听的事件名称必须一致,target就是获取的数据,可以不写target。(自己命名)

通过以上的四步其实就已经实现了最简单的eventbus的实际应用了。

 但是到这儿后,一定要注意一个最容易忽视,又必然不能忘记的东西,那就是清除事件总线eventBus. 不手动清除,它是一直会存在的,这样的话,有个问题就是反复进入到接受数据的组件内操作获取数据,原本只执行一次的获取的操作将会有多次操作。如上我所举的例子,会打印多次传过来的数据。但你想想,实际开发中是不会这么简单的打印这个数据到控制台,本来只会触发并只执行一次,现在变成了多次,这个问题就非常严重了,你们各种脑补具体的项目开发场景吧。

第五步:在vue生命周期beforeDestroy或者destroyed中用vue实例的$off方法清除eventBus

beforeDestroy(){
     bus.$off('click')

三、跨多层级组件通信

A-B-C,三个组件,从爷组件A传参到孙组件C,应该怎么传呢?

(1)通过props一层层传递过去,传到子,给传给孙

(2)用vuex 共享状态(开发大型单页应用)

(3)使用$attrs传参,$listeners监听事件,或者用provide 和 inject

 1、$attrs和$listeners的介绍:

  • $attrs是在vue的2.40版本以上添加的。
  • 所有的非props属性,都可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
  • 多层组件传参使用$attrs,可以使代码更加美观,更加简洁,维护代码的时候更方便。如果使用普通的父子组件传参prop会很繁琐;如果使用vuex会大材小用,只是在这几个组件中使用,没必要使用vuex;使用事件总线eventBus,使用不恰当的话,有可能会出现事件多次执行。
  • 所有组件上的方法绑定子组件都可以通过$listeners接收
  • 详情可查看:vue.js-$attrs
  • 1、vue2中使用$attrs从父组件传递数据给子组件嵌套组件,父组件通过通过$listeners监听子组件的事件 2、vue3把把$attrs和$listeners统一合并到$attrs中

    props的劣势: 父组件传入子组件属性,但子组件没有接收称为非props属性,非props属性默认会加到子组件标签最外层

    inheritAttrs: false的含义是不希望本组件的根元素继承父组件的attribute,同时父组件传过来的属性(没有被子组件的props接收的属性),也不会显示在子组件的dom元素上,但是在组件里可以通过其$attrs可以获取到没有使用的注册属性, inheritAttrs: false是不会影响 style 和 class 的绑定

    $listeners的实例:

    所有组件上的方法绑定子组件都可以通过$listeners接收。它可以通过 v-on="$listeners" 将所有的方法又绑定到组件相应标签,传入内部组件——在创建更高层次的组件时非常有用。

    2、provide 和 inject的介绍与实例:

    provide和inject是在2.2.0 版本新增的,主要为高阶插件/组件库提供用例。这对选项需要一起使用,以允许一个祖先组件向其所有的子孙后代注入一个依赖,不论组件的层次有多深,并在起上下游关系成立的时间里始终生效。

    provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。
    inject 选项应该是:一个字符串数组,或一个对象(属性值是一个对象时,包含from和default默认值)
    

    注意:provide和inject绑定并不是可响应的。这显然不是设计的失误,而是刻意的。如果要详细了解,请参考:API — Vue.js

    我们在上级组件中设置了一个provide:foo,值为beijing,它的作用就是将foo这个变量提供给它的所有下级组件。而在下级组件中通过inject注入了从上级组件中提供的foo变量,那么在下级组件中,就可以直接通过this.foo来访问了

    provide 和 inject的实例:

    如果想要获取父组件parent.vue 的多个属性或方法时,我们应该怎么传呢?是一个个的定义参数传过去吗?

    parent.vue作为一个最外层的根组件,用来存储所有需要的全局数据和状态。我们可以把整个parent.vue实例通过provide对外提供。那么,所有的子组件、孙组件都能共享其数据,方法等。