导语:组件之间的关系不外乎两种, 父子组件和非父子组件,本文将对两类组件之间的通信方式进行详细阐述。

父子组件间的通信

通信方式1(单向绑定): Props down, Events up (建议使用)

Props down, Events up 是指 使用props向子组件传递数据,父组件属性发生变化时,子组件可实时更新视图;子组件发生变化,可以使用$emit发送事件消息,以此向父组件传递变化消息。

props 是单向的,当父组件的属性变化时,将传递给子组件,但子组件中的props属性变化不会影响父组件的属性变化(props属性类型是Object除外)。倘若使用vue1.0的.sync强制双向,那么子组件的更改将会影响到父组件的状态,随着业务的增多,很容易让数据流变得难以理解,最终陷入痛苦的泥潭。因此,vue2.0已经剔除.sync,且不允许在子组件中更改自身的props属性。如果真的需要更改props,那一定是设计方案出了问题,请使用替代方案,如:在data选项或computed选项中再定义一个不同的变量进行数据转换。这是props down。

既然父组件可以通过props像子组件传递信息了,那子组件的数据变化如何通知到父组件呢?

$emit的出现便解决了这一问题,该方法用于 子组件向父组件发送事件消息,可带上子组件的数据信息一起发送,父组件接收到消息后,做出自身相应的变更。vue1.0 和vue2.0均支持$emit。这是events up。

如示例代码1,父组件通过 age(props) 向子组件传递数据信息,子组件拿到后,通过$emit向父组件传递最新状态。如果子组件涉及到可能会对age进行更改,则重新定义一个变量age$进行中转。

【示例代码1】

  1. <body>
  2. <div id="app">
  3. <p>parent age: {{age}}</p>
  4. <p><button @click="changeAge">changeAge to 333</button></p>
  5. <hr>
  6. <child :age="age" @on-age-change="onAgeChange"></child>
  7. </div>
  8. <script>
  9. Vue.component('child', {
  10. template: '<div><p>child age (props选项): {{age}}</p> child age$ (data选项): {{age}}</p> <button @click="changeAge(222)">changeAge to 222</button></div>',
  11. props: {
  12. age: Number
  13. },
  14. data () {
  15. return {
  16. age$: this.age
  17. }
  18. },
  19. methods: {
  20. changeAge (age) {
  21. this.age$ = age
  22. this.$emit('on-age-change', this.age$)
  23. }
  24. }
  25. });
  26.  
  27. new Vue({
  28. el: '#app',
  29. data: {
  30. age: 111
  31. },
  32. methods: {
  33. onAgeChange (val) {
  34. this.age = val
  35. },
  36. changeAge () {
  37. this.age = 333
  38. }
  39. }
  40. })
  41. </script>
  42. </body>

  

通信方式2(双向绑定): .sync 或 v-model(建议在简单场景中使用)

在复杂逻辑组件中,一定不要使用.sync,很容易在N个组件中绕晕。如图, A 是 BC 的父组件,AB和AC都双向了一个共同的props属性(如:model.sync)。B中的Model变化除了影响父组件A,A的变化进而还会影响组件C,这时C就要爆炸了,这Model变化到底来自A,还是来自B。如此,Model的变化变得很难跟踪,增大维护成本。如果B或C还watch model的话,啊呵,足以毁掉你一天的心情了。

父子组件直接双向绑定是个隐式毒虫,但对于某些基础组件来说却是只有益的蜜蜂,可以省掉不少麻烦。一些简单基础的组件,或不需要关心数据流的地方 使用.sync 或 v-model就会是代码显得简洁,且一目了然。

示例代码2 和 示例代码3 效果图:

vue1.0修饰符.sync可以强制props属性双向绑定,如示例代码2,checked为双向绑定,可以轻松完成radio单选组件。

vue2.0中对prop属性进行直接赋值更改会抛错,但如果prop属性类型为object时,仅仅添加或更改props属性内部的属性不会抛错。由于此特性,vue2.0不支持.sync修饰符。

【示例代码2】

  1. <body>
  2. <div id="app">
  3. <radio v-for="item in data" :value="item.value" :checked.sync="checked">{{item.text}}</radio>
  4. </div>
  5. <script>
  6. Vue.component('radio', {
  7. template: '<label><input type="radio" :value="value" v-model="checked"><slot></slot></label>',
  8. props: {
  9. value: {},
  10. checked: {}
  11. }
  12. });
  13. new Vue({
  14. el: '#app',
  15. data: {
  16. checked: '',
  17. data: [
  18. {text: '2G', value: '2G'},
  19. {text: '3G', value: '3G'},
  20. {text: '4G', value: '4G'}
  21. ]
  22. }
  23. })
  24. </script>
  25. </body>

  

vue2.0虽然已经弃用.sync,但有语法糖v-model,其 实质 就是 props down, events up,只是因为v-model隐藏了数据流,因此唤其为双向绑定。 父组件向下使用props隐式属性value将数据传递数据给子组件,子组件使用$emit('input')向上将数据返回给父组件。但正如上文所说,对于基础组件或不关心数据流的组件使用双向绑定是糟粕中的小蜂蜜,书写简洁,清晰明了。

【示例代码3】

  1. <body>
  2. <div id="app">
  3. <radio v-for="item in data" :label="item.value" v-model="checked">{{item.text}}</radio>
  4. </div>
  5. <script>
  6. Vue.component('radio', {
  7. template: '<label><input type="radio" :value="label" v-model="checked"><slot></slot></label>',
  8. props: {
  9. label: {},
  10. value: {}
  11. },
  12. computed: {
  13. checked: {
  14. get () {
  15. return this.value
  16. },
  17. set (val) {
  18. this.$emit('input', val)
  19. }
  20. }
  21. }
  22. });
  23. new Vue({
  24. el: '#app',
  25. data: {
  26. checked: '',
  27. data: [
  28. {text: '2G', value: '2G'},
  29. {text: '3G', value: '3G'},
  30. {text: '4G', value: '4G'}
  31. ]
  32. }
  33. })
  34. </script>
  35. </body>

  

拆解语法糖v-model,如示例代码4。已熟知的朋友可以略过。

代码第4行 <child :value="info" @input="dataChange"> </child>更为 <child v-model="info"></child> 效果一样。

【示例代码4】

  1. <body>
  2. <div id="app">
  3. <p>parent info: {{info}}</p>
  4. <child :value="info" @input="dataChange"></child>
  5. <p><button @click="changeInfo">changeInfo from parent</button></p>
  6. </div>
  7. <script>
  8. Vue.component('child', {
  9. template: '<div><p>child data: {{data}}</p> <button @click="changeData">changeData from child</button></div>',
  10. props: {
  11. value: {}
  12. },
  13. computed: {
  14. data: {
  15. get () {
  16. return this.value
  17. },
  18. set (val) {
  19. this.$emit('input', val)
  20. }
  21. }
  22. },
  23. methods: {
  24. changeData () {
  25. this.data = 'This is a child component!'
  26. }
  27. }
  28. });
  29. new Vue({
  30. el: '#app',
  31. data: {
  32. info: 'This is a original component!'
  33. },
  34. methods: {
  35. dataChange (info) {
  36. this.info = info
  37. },
  38. changeInfo () {
  39. this.info = 'This is a parent component!'
  40. }
  41. }
  42. })
  43. </script>
  44. </body>

  

 

通信方式3: $broadcast 和 $dispatch(不建议使用)

只有vue1.0中才有这两种方法。

  $dispatch首先会触发自身实例,冒泡向上,直到某个父组件收到$dispatch消息。如果子组件内部使用了$dispatch,那么该组件的 父组件链在写监听事件时都必须格外小心,必须得有父组件截获该消息,否则会一直冒泡。这是一项非常危险的行为,因为,父组件链中的组件 很难关注到所有子组件的dispatch消息,随着$dispatch在组件中增多,顶层组件或中间组件想知道消息来自哪个子组件变得异常艰辛,事件流跟踪困难,痛苦深渊由此开启。

$broadcast会向每一个子树路径发送消息,一条路径某个组件接收消息,则该路径停止向下发送消息,其它路径规则一样。同理$dispatch,随着通信的增加,消息的增多,子组件也将很难跟踪监听的消息到底来自哪个父组件。不注意的话,最后上演一场的 寻亲记,想来也是持久精彩的。

$dispatch 和 $broadcast 都已在vue2.0中被弃用,如果实在想坚持使用,也可通过$emit进行模拟,通过apply或call改变this,递归便利。

非父子组件间的通信

1. 状态管理方案 vuex(建议使用)

适合复杂数据流管理。详细使用方法见如下站点,

https://github.com/vuejs/vuex

2. 中央数据总线(建议在简单的场景中使用)

创建一个单独的空Vue实例(Events = new Vue()),作为事件收发器,在该实例上进行$emit, $on, $off等操作。适用场景:a. 使用Events.$on的组件不关心事件具体来源; b. 事件处理程序 执行与否 或 重复执行 都没有副作用(如刷新、查询等操作)。如示例代码5,

【示例代码5】

  1. <body>
  2. <div id="app">
  3. <h5>A组件</h5>
  4. <a-component></a-component>
  5. <h5>B组件</h5>
  6. <b-component></b-component>
  7. </div>
  8. <script>
  9. var Events = new Vue()
  10. Vue.component('AComponent', {
  11. template: '<button @click="changeBName">change B name</button>',
  12. methods: {
  13. changeBName () {
  14. Events.$emit('on-name-change', 'The name is from A component!')
  15. }
  16. }
  17. });
  18. Vue.component('BComponent', {
  19. template: '<p>B name: {{name}}</p>',
  20. data () {
  21. return {
  22. name: 'sheep'
  23. }
  24. },
  25. created () {
  26. Events.$on('on-name-change', (name) => {
  27. this.name = name
  28. })
  29. }
  30. });
  31. new Vue({
  32. el: '#app'
  33. })
  34. </script>
  35. </body>

  

为什么说只适用简单场景呢? vue组件化的开发模式意味着一个组件很可能被多次实例化。请看下文分析,

假设 A组件使用Events.$emit('event'), 在同一个界面被实例化了两次,现在的需求是,组件A实例1触发消息'event'时,组件B根据消息'event'相应更新,组件A实例2触发消息'event',组件B不能根据消息'event'进行相应的更新。这时,因为组件B使用的是Events.$on('event')就搞不清是由A组件实例1触发的消息'event', 还是A组件实例2触发的。

因此,使用该方法时,最好保证组件在同一界面只会被渲染一次,或这不需要关心Events.$emit由哪个实例触发。

3. 利用对象的值传递特性

适用场景:列表中,需要更改某条数据的某项信息。当一个变量向另一个变量复制引用类型的值时,复制的值实际上是一个指针,指向存储在堆中的同一个对象。因此,改变其中一个变量就会影响另一个变量。  如,在一个表格列表中,如何在 N行X列 更改 N行Y列 的数据?

【示例代码6】

  1. <body>
  2. <div id="app">
  3. <table>
  4. <tr>
  5. <th v-for="title in table.head">{{title}}</th>
  6. </tr>
  7. <tr v-for="item in table.data">
  8. <td>{{item.name}}</td>
  9. <td><status :value="item.status"></status></td>
  10. <td><t-switch :item="item">切换</t-switch></td>
  11. </tr>
  12. </table>
  13. </div>
  14. <script>
  15. Vue.component('status', {
  16. template: '<span>{{value}}</span>',
  17. props: {
  18. value: {}
  19. }
  20. });
  21.  
  22. Vue.component('t-switch', {
  23. template: '<button @click="switchStatus">切换状态</button>',
  24. props: {
  25. item: {}
  26. },
  27. methods: {
  28. switchStatus () {
  29. this.item.status = this.item.status === '有效' ? '无效' : '有效'
  30. }
  31. }
  32. });
  33. new Vue({
  34. el: '#app',
  35. data: {
  36. table: {
  37. head: ['广告名称', '状态', '操作'],
  38. data: []
  39. }
  40. },
  41. ready () {
  42. var timer = setTimeout(() => {
  43. this.table.data = [
  44. {name: 'adName1', status: '无效'},
  45. {name: 'adName2', status: '有效'},
  46. {name: 'adName3', status: '无效'}
  47. ]
  48. clearTimeout(timer)
  49. }, 1000)
  50. }
  51. })
  52. </script>
  53. </body>

  

4. Vue-rx

https://github.com/vuejs/vue-rx

 https://github.com/Reactive-Extensions/RxJS

简而言之,无论是vue1.0,还是vue2.0,为保证清晰的数据流 和 事件流,父子组件通信遵循“props down, events up”的原则。非父子组件根据不同场景选择不同的方案,大部分情况依然建议使用vuex状态管理方案。特别复杂的场景,建议使用vue-rx。

————————————————————

只有足够努力,才能看起来毫不费力!

Vue.js组件之间的通信的更多相关文章

  1. 【转】vue父子组件之间的通信

    vue父子组件之间的通信 在vue组件通信中其中最常见通信方式就是父子组件之中的通性,而父子组件的设定方式在不同情况下又各有不同.最常见的就是父组件为控制组件子组件为视图组件.父组件传递数据给子组件使 ...

  2. vue中组件之间的通信

    一.vue中组件通信的种类 父组件向子组件的通信 子组件向父组件的通信 隔代组件之间的通信 兄弟 组件 之间的通信 二.实现通信的方式  props vue自定义的事件 消息订阅与发布 vuex sl ...

  3. js组件之间的通信

    应用场景: 1.在刷微博的时候,滑到某个头像上,会出现一张usercard(用户名片), 微博列表区有这个usercard, 推荐列表也有这个usercard,评论区也有. 2.在网上购物时,购物车安 ...

  4. vue.js 组件之间传递数据

    前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用.如何传递数据也成了组件的重要知识点之一. 组件 组件与组件之间,还存在着不同的关 ...

  5. vue.js组件之间的通讯-----父亲向儿子传递数据,儿子接收父亲的数据

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. vue.js组件之间通讯--父组件调用子组件的一些方法,子组件暴露一些方法,让父组件调用

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  7. vue.js组件之间通讯的数据双向绑定----父亲把数据传递给儿子,儿子更改数据后,重新发送给父亲,父亲数据更改后,属性会重新发送个儿子,儿子刷新新数据

    vue组件是相互独立的,如果要交互通讯,这时候,就需要组件之间数据互通了 往常我们讲的都是数据传递,子传父,父传子,都没有说子和父,父与子之间的数据互通 父亲传递给儿子数据,儿子触发一个父亲方法,将最 ...

  8. vue父子组件之间的通信

    利用props在子组件接受父组件传过来的值1.父组件parentComp.vue <template> <childComp :fromParentToChild="fro ...

  9. Vue.js组件之间的调用

    index.html: <div id="app"></div> 运行完index.html之后自动寻找运行main.js文件 main.js: impor ...

随机推荐

  1. Ionic2 + Angular4 + JSSDK开发中的若干问题汇总

    前景 目前微信公众号程序开发已经相当火热,客户要求自己的系统有一个公众号,已经是一个很常见的需要. 使用公众号可以很方便的便于项目干系人查看信息和进行互动,还可以很方便录入一些电脑端不便于录入的数据, ...

  2. MongoDB Java Driver 3.4操作

    导入jar包 <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-dr ...

  3. sublime设置 快捷键(自动换行)

    一.菜单view > word wrap选上就好了 二.如果让编辑器默认是自动换行的话把它保存到配置中 Preference > Settings-User插入以下一行配置 "w ...

  4. 基于webpack2.x的vue2.x的多页面站点

    vue的多页面 依旧使用vue-cli来初始化我们的项目 然后修改主要目录结构如下: ├── build │   ├── build.js │   ├── check-versions.js │   ...

  5. 回锅的美食:JSP+EL+JSTL大杂烩汤

    title: Servlet之JSP tags: [] notebook: javaWEB --- JSP是什么 ? JSP就是Servlet,全名是"JavaServer Pages&qu ...

  6. CachedRowSet使用

    public interface CachedRowSet extends RowSet,Joinable 所有标准 CachedRowSet 实现都必须实现的接口.Sun Microsystems ...

  7. Python爬虫学习之获取网页源码

    偶然的机会,在知乎上看到一个有关爬虫的话题<利用爬虫技术能做到哪些很酷很有趣很有用的事情?>,因为强烈的好奇心和觉得会写爬虫是一件高大上的事情,所以就对爬虫产生了兴趣. 关于网络爬虫的定义 ...

  8. c# 对加密的MP4文件进行解密

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  9. Net分布式系统之六:微服务之API网关

    本人建立了个人技术.工作经验的分享微信号,计划后续公众号同步更新分享,比在此更多具体.欢迎有兴趣的同学一起加入相互学习.基于上篇微服务架构分享,今天分享其中一个重要的基础组件“API网关”. 一.引言 ...

  10. mac系统下给文件夹加密方法

    电脑里我们往往会有许多隐私的文件,不希望被别人看到,在过去的Windows电脑里,我们习惯性的会在文件夹中将该文件隐藏,但是这个隐藏是不安全的,遇到稍微会点电脑技术的人就可以给你解开,安全性不高,ma ...