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

父子组件间的通信

通信方式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】

<body>
<div id="app">
<p>parent age: {{age}}</p>
<p><button @click="changeAge">changeAge to 333</button></p>
<hr>
<child :age="age" @on-age-change="onAgeChange"></child>
</div>
<script>
Vue.component('child', {
template: '<div><p>child age (props选项): {{age}}</p> child age$ (data选项): {{age}}</p> <button @click="changeAge(222)">changeAge to 222</button></div>',
props: {
age: Number
},
data () {
return {
age$: this.age
}
},
methods: {
changeAge (age) {
this.age$ = age
this.$emit('on-age-change', this.age$)
}
}
}); new Vue({
el: '#app',
data: {
age: 111
},
methods: {
onAgeChange (val) {
this.age = val
},
changeAge () {
this.age = 333
}
}
})
</script>
</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】

<body>
<div id="app">
<radio v-for="item in data" :value="item.value" :checked.sync="checked">{{item.text}}</radio>
</div>
<script>
Vue.component('radio', {
template: '<label><input type="radio" :value="value" v-model="checked"><slot></slot></label>',
props: {
value: {},
checked: {}
}
});
new Vue({
el: '#app',
data: {
checked: '',
data: [
{text: '2G', value: '2G'},
{text: '3G', value: '3G'},
{text: '4G', value: '4G'}
]
}
})
</script>
</body>

  

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

【示例代码3】

<body>
<div id="app">
<radio v-for="item in data" :label="item.value" v-model="checked">{{item.text}}</radio>
</div>
<script>
Vue.component('radio', {
template: '<label><input type="radio" :value="label" v-model="checked"><slot></slot></label>',
props: {
label: {},
value: {}
},
computed: {
checked: {
get () {
return this.value
},
set (val) {
this.$emit('input', val)
}
}
}
});
new Vue({
el: '#app',
data: {
checked: '',
data: [
{text: '2G', value: '2G'},
{text: '3G', value: '3G'},
{text: '4G', value: '4G'}
]
}
})
</script>
</body>

  

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

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

【示例代码4】

<body>
<div id="app">
<p>parent info: {{info}}</p>
<child :value="info" @input="dataChange"></child>
<p><button @click="changeInfo">changeInfo from parent</button></p>
</div>
<script>
Vue.component('child', {
template: '<div><p>child data: {{data}}</p> <button @click="changeData">changeData from child</button></div>',
props: {
value: {}
},
computed: {
data: {
get () {
return this.value
},
set (val) {
this.$emit('input', val)
}
}
},
methods: {
changeData () {
this.data = 'This is a child component!'
}
}
});
new Vue({
el: '#app',
data: {
info: 'This is a original component!'
},
methods: {
dataChange (info) {
this.info = info
},
changeInfo () {
this.info = 'This is a parent component!'
}
}
})
</script>
</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】

<body>
<div id="app">
<h5>A组件</h5>
<a-component></a-component>
<h5>B组件</h5>
<b-component></b-component>
</div>
<script>
var Events = new Vue()
Vue.component('AComponent', {
template: '<button @click="changeBName">change B name</button>',
methods: {
changeBName () {
Events.$emit('on-name-change', 'The name is from A component!')
}
}
});
Vue.component('BComponent', {
template: '<p>B name: {{name}}</p>',
data () {
return {
name: 'sheep'
}
},
created () {
Events.$on('on-name-change', (name) => {
this.name = name
})
}
});
new Vue({
el: '#app'
})
</script>
</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】

<body>
<div id="app">
<table>
<tr>
<th v-for="title in table.head">{{title}}</th>
</tr>
<tr v-for="item in table.data">
<td>{{item.name}}</td>
<td><status :value="item.status"></status></td>
<td><t-switch :item="item">切换</t-switch></td>
</tr>
</table>
</div>
<script>
Vue.component('status', {
template: '<span>{{value}}</span>',
props: {
value: {}
}
}); Vue.component('t-switch', {
template: '<button @click="switchStatus">切换状态</button>',
props: {
item: {}
},
methods: {
switchStatus () {
this.item.status = this.item.status === '有效' ? '无效' : '有效'
}
}
});
new Vue({
el: '#app',
data: {
table: {
head: ['广告名称', '状态', '操作'],
data: []
}
},
ready () {
var timer = setTimeout(() => {
this.table.data = [
{name: 'adName1', status: '无效'},
{name: 'adName2', status: '有效'},
{name: 'adName3', status: '无效'}
]
clearTimeout(timer)
}, 1000)
}
})
</script>
</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. PHP count() 函数

    count() 函数计算数组中的单元数目或对象中的属性个数. 对于数组,返回其元素的个数,对于其他值,返回 1.如果参数是变量而变量没有定义,则返回 0.如果 mode 被设置为 COUNT_RECU ...

  2. cas 代理认证配置

    注:本文转自http://www.ichatter.net/2013/03/21/385/ CAS(Central Authentication Service)框架是一个开源的单点登陆框架.最近公司 ...

  3. centos下的activemq的配置及PHP的使用

    一.安装JDK 1.下载JDK(官网:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.ht ...

  4. 详解Java动态代理机制(二)----cglib实现动态代理

    上篇文章的结尾我们介绍了普通的jdk实现动态代理的主要不足在于:它只能代理实现了接口的类,如果一个类没有继承于任何的接口,那么就不能代理该类,原因是我们动态生成的所有代理类都必须继承Proxy这个类, ...

  5. elasticsearch系列(二) esrally压测

    环境准备 linux centOS(工作环境) python3.4及以上 pip3 JDK8 git1.9及以上 gradle2.13级以上 准备过程中的坑 这些环境准备没什么太大问题,都是wget下 ...

  6. 学习MVC之租房网站(八)- 前台注册和登录

    在上一篇<学习MVC之租房网站(七)-房源管理和配图上传>完成了在后台新增.编辑房源信息以及上传房源配图的功能.到此后台开发便告一段落了,开始实现前台的功能,也是从用户的登录.注册开始. ...

  7. tomcat流程原理解析

    tomcat的启动是通过Bootstrap类的main方法(tomcat6开始也可以直接通过Catlina的main启动) Bootstrap的启动 Bootstrap的main方法先new了一个自己 ...

  8. php高性能开发阅读笔记

    1.http请求与响应的简单流程 上图简单的描述了一个http请求与响应的过程,首先是用户请求过程,这是该生命周期的第一部分,用户发起请求,经过路由器与ips网关和dns服务器(域名服务器),通过we ...

  9. Bootstrap实现轮播图

    第一步:设计轮播图容器:div.carousel,添加slide平滑切换,并定义id,方便后面采用data属性来触发 <div class='carousel slide' id="t ...

  10. 【JAVAWEB学习笔记】28_jqueryAjax:json数据结构、jquery的ajax操作和表单校验插件

    Ajax-jqueryAjax 今天内容: 1.json数据结构(重点) 2.jquery的ajax操作(重点) 3.jquery的插件使用   一.json数据结构 1.什么是json JSON(J ...