Vue组件通信

简介

组件是Vue的核心,而组件间的状态管理和数据传递是开发绕不开的问题。在Vue中,组件和组件之间是相互独立的,所以需要一定的方法才能进行Vue的组件间的通信。

基础技能 props和$emit

如果你连这个都不会用的话就一定要好好看一下这篇文章和官方文档

结合场景分析:现在我们有一个获取客户列表的功能,并且要展示客户的信息。
那么我们先来开发一个customer-item的客户详情组件

<template>
<!-- 客户信息组件 -->
  <div>
    <input type="text" v-model="selfName" /> <button @click="rename">Rename</button>{{name}}
  </div>
</template>

<script>
export default {
  props: {
    name: String,
    index: Number
  },
  data() {
    return {
      selfName: ''
    }
  },
  mounted() {
    this.selfName = this.name
  },
  methods: {
    rename() {
      this.$emit('rename', this.index, this.selfName)
    }
  },
}
</script>


customer可以通过props来接收父组件向下通过属性传播的值,这就是父向子的一个通信。而子组件如果想要更新父组件的状态,是不能直接去向上修改状态的。这时候就需要通过事件来完成这个操作,也就是通过$emit来触发父组件定义的一个事件(在上面代码中就是触发父组件的rename事件),同时可以将要修改的数据当作参数传递,即可在父组件中完成状态更新。

进阶技能1 $attrs$listeners

首先,这两个是Vue 2.4.0 版本新出的属性,所以如果没有接触过的话就需要好好补充一下自己的技能包了。

这两个属性可以说是 props和$emit 的一个补充增强。按照我们之前的写法,父组件像自组件传递参数其实是按照属性的形式向下传递。那么其实对于自组件而言,我们可以无需关心父组件具体向我们传递了哪些东西,我们能够拿到这些属性值即可。

第二个cutomer组件:

<!--父组件-->
<Customer v-for="(item,index) in customers" :key="item.name" :index="index" @add="addOrder" :name="item.name" :order="item.order"/> 

<!--子组件-->
<template>
  <div>
    <div>姓名: {{$attrs.name}}</div>
    <div>订单数: {{$attrs.order}}<button @click="add">增加</button></div> 
  </div>
</template>
<script>
export default {
  data() {return {}},
  methods: {
    add() {
      this.$listeners.add(this.$attrs.index)
    }
  },
}
</script>

这样,自组件就通用了很多,不需要在props去声明那么多的属性,而触发方法的emit也可以通过$listeners来引用父组件中绑定在自组件的事件,即可完成父子组件的通信工作。而这两个属性 最便捷的作用还是可以通过v-bind="$attrs"来完成$attrs的一个属性传递和v-on="$listeners"的一个事件传递来实现子组件与祖先组件的一个通信。如上述的代码,我可以改造为:

<!--Customer组件-->
<template>
  <div>
    <div>姓名: {{$attrs.name}}</div>
    <Order v-bind="$attrs" v-on="$listeners"/>
  </div>
</template>
<script>
import Order from './Order.vue'
export default {
  data() {return {}},
  components: {
    Order
  }
}
</script>
<!--Order组件-->
<template>
  <div>
    订单数: {{$attrs.order}}<button @click="add">增加</button>
  </div>
</template>
<script>
export default {
  methods: {
    add() {
      this.$listeners.add(this.$attrs.index)
    }
  },
}
</script>

那么,在设计一些较为复杂的组件时,使用$attrs$listeners要比props+$emit的组合要好用太多了

Tips: 在使用$attrs来进行向下的属性传递时,会默认将这些属性附加到自组件上,如图

inheritAttrs.png

这种行为是默认的,如果不希望默认这种行为,则可以通过设置inheritAttrs为false来阻止这个默认行为:


inheritAttrs2.png

进阶技能2 eventBus($emit, $on

上面的两种方法都是均常用于长辈组件和晚辈组件的数据通信,而对于兄弟组件而言,要想通过上述的方法实现效果,就需要父组件做一个中转站(父组件用来管理状态,A组件修改状态,通过事件通知父组件,父组件再修改状态来达到修改B组件状态的效果),这无疑是一个没必要的开销,而且如果兄弟组件多的时候,父组件中的状态会非常的冗余。

eventBus可以作为一个事件的转发中心,对于组件而言,均可以注册eventBus,而某个组件触发事件时,注册了这个事件的组件均会触发事件并且执行对应的方法。这个过程可以通过下面这个图理解:

eventBus.png

那么其实在eventBus中,我们不再关心组件和组件间具体是父子还是兄弟还是祖先的关系,而是将重点放在通过事件来进行组件间的交互。当然,eventBus还是最常用语兄弟组件或者跨级组件这种场景。

// 先创建一个eventBus,Vue的实例对象就是一个天然的eventBus

import Vue from 'vue'
const eventVue = new Vue()
export default eventVue

// 组件A
<script>
import eventBus from './eventBus'
export default {
  data() {
    return {
      msg: ''
    }
  },
  methods: {
    sendMsg() {
    // 触发事件
      eventBus.$emit('sendMsg', this.msg)
    }
  },
}
</script>

// 组件B
<script>
import eventBus from './eventBus'
export default {
  data() {
    return {
      msg: ''
    }
  },
  mounted() {
    // 注册事件
    eventBus.$on('sendMsg', val => {
      this.msg = val
    })
  }
}
</script>

进阶技能3 provide&inject

掌握上面的三个技能,在大多数的组件交互中已经够用了。不过在组件给其他组件传递状态时,不管是使用props还是$attrs都是有一些繁琐。在Vue 2.2 的时候新增的provide,inject属性就很适合这种数据传递的场景。

仅从字面意思上来理解这个东西,组件A提供几个状态,组件B注入这些状态。不过仅在使用provide&inject是没有办法实现数据的响应式的。最基本的用法如下所示:

语法:
provide:Object | () => Object
inject:Array<string> | { [key: string]: string | Symbol | Object }


// 父组件
<script>
import Customer from '../components/provide/Customer'
export default {
  data() {
    return {
      info: {
        name: 'wyh',
        order: 18
      }
    }
  },
  components: {
    Customer
  },
  provide() {
    return {
      name: this.info.name,
      order: this.info.order
    }
  }
}
</script>

// 子组件
<template>
  <div>
    姓名: {{name}}
    <Order />
  </div>
</template>
export default {
  inject: ['name']
}

// 孙组件
export default {
  inject: ['order']
}

效果:


provide1.png

inject注入状态的写法一种是上面的字符串数组形式的。
另一种是对象形式。如果你对当前组件中注入的这个状态有更多修饰时使用,比如重命名等,代码如下:

// 重命名
inject: {
  selfOrder: 'order'
}
// 设置默认值
inject: {
 order: {
    from: 'order',
    default: 17
  }
}

provide和inject不是响应式的,不过如果传入的是一个可以监听的对象,那么其属性就也可以实现成响应式。这里要使用Vue提供的静态方法Vue.observable(2.6版本后可用)代码如下:

// 父组件
provide() {
    // 生成响应式对象
    this.data = Vue.observable(this.info)
    return {
      name: this.info.name,
      order: this.info.order,
      data: this.data
    }
  }

// 子组件
<template>
  <div>
    姓名: {{name}} <br />
    响应式的姓名: {{data.name}}
    <Order />
  </div>
</template>

<script>
import Order from './Order'
export default {
  inject: ['name', 'data'],
  components: {
    Order
  }
}
</script>

效果:


observable.gif

最终技能 Vuex

Vuex的内容就太多了。不如放几个思考题。

  1. Vuex如何进行状态管理,它的map工具是否会使用
  2. Vuex是如何完成状态更新的
  3. mutation和action的区别,为何mutation一定要是同步的
  4. Vuex模块化方案
  5. 能否对比一下redux

还有个黑科技 $ref $parent $children

由于Vue可以通过上面的三个属性来获取父组件,子组件的实例,也就可以直接去进行一些组件间的交互了。比如我修改一下props demo中的代码:

// 修改名称代码黑科技
rename() {
      // this.$emit('rename', this.index, this.selfName)
      this.$parent.$set(this.$parent.$data.names, this.index, this.selfName)
    }

依然是可以进行组件间的数据交互的。不过黑科技就是黑科技,还是不推荐使用,除非上面的几种形式均不满足要求的时候。

麻烦点个start咯 github地址 这个代码后续会完善Vuex的一些学习笔记

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

推荐阅读更多精彩内容

  • 对于vue来说,组件之间的消息传递是非常重要的,下面是我对组件之间消息传递的各种方式的总结,总共有8种方式。 1....
    edc余悸阅读 355评论 0 3
  • 组件作为Vue中的核心概念,是值得我们深入研究的课题之一,通过研究它,我们可以理解更高深的思想,可以提升自己的开发...
    北辰_狼月阅读 761评论 2 7
  • 父子组件通信 1、父子组件通过prop传递数据 父组件可以将一条数据传递给子组件,这条数据可以是动态的,父组件的数...
    视觉派Pie阅读 1,264评论 0 18
  • 前言 组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引...
    用技术改变世界阅读 2,164评论 1 3
  • 摘要: 总有一款合适的通信方式。 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有。 前言 组件是 v...
    Fundebug阅读 15,574评论 3 57