Vue3组件通讯

vue3组件之间并不是孤立的,它们之间是需要通信的,正是这种组件间的相互通信才构成了页面上用户行为交互的过程。

一. 父组件向子组件通信

父组件向子组件通信可以理解成:

  • 父组件向子组件传值。
  • 父组件调用子组件的方法。
  1. props
    利用props属性可以实现父组件向子组件传值,eg :
    const componentC={
        props:['info'],//子组件使用props接收
        data(){
            return {
                str:'I am C'
            }
        },
        template: `<span>{{ str }} : {{info}} </span>`
    }

    //定义名为compontentB的父组件,
    const componentB={
        data(){
            return {
               str :'I am B',
               info:'data from B'
            }
        },
        template: `
          <div>
            {{str}}
            <div>
              <component-c :info='info' />
            </div>    
          </div>
        `,
        components : {
            'component-c': componentC,
        }
    }

  props不仅可以传字符串类型的值,像数组、对象、布尔值都可以传递,在传递时props也可以定义成数据校验的形式,以此来限制接收的数据类型,提升规范性,避免得到意想之外的值,type可以是下列原生构造函数中的一个: String、Number、Boolean、Array、Object、Date、Function、Symbol。type还可以是一个自定义的构造函数,并且通过instanceof来进行检查确认
  如果props传递的是一个动态值,每次父组件的info发生更新时,子组件中接收的props都将会刷新为最新的值。这意味着,我们不应该在一个子组件内部改变props,如果这样做了,Vue会在浏览器的控制台中发出警告。
  Vue中父传子的方式形成了一个单向下行绑定,叫作单向数据流。父级props的更新会向下流动到接收这个props的子组件中,触发对应的更新,但是反过来则不行。这样可以防止有多个子组件的父组件内的值被修改时,无法查找到哪个子组件修改的场景,从而导致应用中的数据流向无法清晰地追溯。

  1. $refs
    利用Vue实例的$refs属性可以实现父组件调用子组件的方法,eg:
    const componentC={
        props:['info'],//子组件使用props接收
        data(){
            return {
                str:'I am C'
            }
        },
        template: `<span>{{ str }} : {{info}} </span>`,
        methods:{
            doFunc(){
                console.info('do func C')
            }
        }
    }

    //定义名为compontentB的父组件,
    const componentB={
        data(){
            return {
               str :'I am B',
               info:'data from B'
            }
        },
        template: `
          <div>
            {{str}}
            <div>
              <component-c :info='info'  ref='componentC'/>
            </div>    
          </div>
        `,
        components : {
            'component-c': componentC,
        },
        mounted(){
               this.$refs.componentC.doFunc()
        }
    }

  在Vue中,也可以给原生的DOM元素绑定ref值,这样通过this.$refs拿到的就是原生的DOM对象

 <button ref="btn"></button>

二. 子组件向父组件通信

  与父组件向子组件通信不同的是,子组件调用父组件方法的同时就可以向父组件传值,使用$emit方法和自定义事件。

1.$emit
  $emit 方法的主要作用是触发当前组件实例上的事件,所以子组件调用父组件方法就可以理解成子组件触发了绑定在父组件上的自定义事件。

//子组件
mounted():{
  this.$emit('myFunction','hi')
}
//父组件
{
  template: '<component-c @myFunction='myFunction' />',
  methods: {
       //定义父组件需要被子组件调用的方法
     myFunction(data){
        console.log('来自子组件的调用',data) 
     }
  }
}

2.$parent
  这种方法比较直观,可以直接操作父子组件的实例,在子组件中直接通过this.$parent获取父组件的实例,从而调用父组件中定义的方法,类似于以上介绍的通过$refs获取子组件的实例。

//子组件
mounted() {
   //直接采用$parent调用
   this.$parent.myFunction('$parent方法调用')
}
//父组件
methods:{
  myFunction(data){
    console.log('来自子组件的调用',data)
  }
}

  Vue并不推荐以这种方法来实现子组件调用父组件,由于一个父组件可能会有多个子组件,因此这种方法对父组件中的状态维护是非常不利的,当父组件的某个属性被改变时,无法以循规溯源的方式去查找到底是哪个子组件改变了这个属性。因此,请有节制地使用$parent方法,它的主要目的是作为访问组件的应急方法。推荐使用$emit方法实现子组件向父组件的通信。

三. 父子组件的双向数据绑定与自定义v-model

  父组件可以使用props给子组件传值,当父组件props更新时也会同步给子组件,但是子组件无法直接修改父组件的props,这其实是一个单向的过程。但是在一些情况下,我们可能会需要对一个props进行“双向绑定”,即子组件的值更改后,父组件也同步进行更改。
在父组件的data中定义info属性,并且通过v-model的方式传给了子组件,代码如下:

 <component-d v-model:info="info" />

这里使用$emit方法,在子组件中,给按钮button绑定了一个单击事件,在事件回调函数中,采用如下代码:

this.$emit('update:info','Tom')

  这样更新就会同步到父组件的props中,调用$emit方法实际上就是触发一个父组件的方法,这里的update是固定写法,代表更新,而:info表示更新info这个prop,第二个参数Tom表示更新的值。这就完成了父子组件的“双向绑定”。

四. 非父子关系组件的通信

  1. 兄弟组件的通信
    同一个父组件B的兄弟组件C和D而言,可以借助父组件B这个桥梁,实现兄弟组件的通信
    在D组件中通过$emit调用父组件B的方法,同时在父组件中修改data中的infoFromD(infoFromD属性绑定到了组件C的infoFromD),同时也影响到了作为props传递给C组件的infoFromD,这就实现了兄弟组件的通信。
  2. 事件总线EventBus和mitt
    在Vue 2中,可以采用EventBus这种方法,实际上就是将沟通的桥梁换成自己,同样需要有桥梁作为通信中继。就像是所有组件共用相同的事件中心,可以向该中心发送事件或接收事件,所有组件都可以上下平行地通知其他组件。
    vue2中的实现
//定义全局中央事件总线
var EventBus =  new Vue();
//将事件总线赋值给Vue.prototype 这样所有组件就都能访问到了
Vue.prototype.$EventBus =  EventBus;

//监听事件
this.$EventBus.$on('eventBusEvent',(data)=>{
   //处理逻辑
}).bind(this)

//触发事件
this.$EventBus.$emit('eventBusEvent','我的事件总线')

  通过new Vue()实例化了一个Vue的实例,这个实例是一个没有任何方法和属性的空实例,称其为:中央事件总线(EventBus),然后将其赋值给Vue.prototype.$EventBus,使得所有的组件都能够访问到。
  $on方法和$emit方法其实都是Vue实例提供的方法,这里的关键点就是利用一个空的Vue实例来作为桥梁,实现事件分发,它的工作原理是发布/订阅方法,通常称为Pub/Sub,也就是发布和订阅的模式。
  在Vue 3中,由于取消了Vue中全局变量Vue.prototype.$EventBus这种写法,所以采用EventBus这种事件总线来进行通信已经无法使用,取而代之,可以采用第三方事件总线库mitt。
  在页面中引入mitt的JavaScript文件或者在项目中采用import方式引入mitt,

<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
 //or
 import mitt from 'mitt'

mitt的使用方法和EventBus非常类似,同样是基于Pub/Sub模式,并且更加简单,可以在需要进行通信的地方直接使用

const emitter = mitt();
//监听
emitter.on('eventBusEvent', (data) => {
   // todo 
})
//触发
clickCallback() {
    emitter.emit('eventBusEvent', '我是...')
}

  与EventBus相比,mitt的方式无须创建全局变量,使用起来更加简单。
  事件总线的方式进行通信使用起来非常简单,可以实现任意组件之间的通信,其中没有多余的业务逻辑,只需要在状态变化组件触发一个事件,随后在处理逻辑组件监听该事件即可。这种方法非常适合小型的项目,但是对于一些大型的项目,要实现复杂的组件通信和状态管理,就需要使用Vuex了。

五. provide / inject

  通常,当需要从父组件向子组件传递数据时,我们使用props。想象一下这样的结构:有一些深度嵌套的组件,深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将props沿着组件链逐级传递下去,可能会很麻烦。
  对于这种情况,我们可以使用一对provide(提供)和inject(注入)。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个provide选项来提供数据,子组件有一个inject选项来开始使用这些数据

//父组件
{
  provide: {
    user: ' john'
 }
}
//子组件
{
  inject: ['user'],
  created(){
    this.user;
 }
}

要访问组件实例data中的属性,我们需要将provide转换为返回对象的函数。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容