Vue.js组件之间的通信
导语:组件之间的关系不外乎两种, 父子组件和非父子组件,本文将对两类组件之间的通信方式进行详细阐述。
父子组件间的通信
通信方式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(建议使用)
适合复杂数据流管理。详细使用方法见如下站点,
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组件之间的通信的更多相关文章
- 【转】vue父子组件之间的通信
vue父子组件之间的通信 在vue组件通信中其中最常见通信方式就是父子组件之中的通性,而父子组件的设定方式在不同情况下又各有不同.最常见的就是父组件为控制组件子组件为视图组件.父组件传递数据给子组件使 ...
- vue中组件之间的通信
一.vue中组件通信的种类 父组件向子组件的通信 子组件向父组件的通信 隔代组件之间的通信 兄弟 组件 之间的通信 二.实现通信的方式 props vue自定义的事件 消息订阅与发布 vuex sl ...
- js组件之间的通信
应用场景: 1.在刷微博的时候,滑到某个头像上,会出现一张usercard(用户名片), 微博列表区有这个usercard, 推荐列表也有这个usercard,评论区也有. 2.在网上购物时,购物车安 ...
- vue.js 组件之间传递数据
前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用.如何传递数据也成了组件的重要知识点之一. 组件 组件与组件之间,还存在着不同的关 ...
- vue.js组件之间的通讯-----父亲向儿子传递数据,儿子接收父亲的数据
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- vue.js组件之间通讯--父组件调用子组件的一些方法,子组件暴露一些方法,让父组件调用
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- vue.js组件之间通讯的数据双向绑定----父亲把数据传递给儿子,儿子更改数据后,重新发送给父亲,父亲数据更改后,属性会重新发送个儿子,儿子刷新新数据
vue组件是相互独立的,如果要交互通讯,这时候,就需要组件之间数据互通了 往常我们讲的都是数据传递,子传父,父传子,都没有说子和父,父与子之间的数据互通 父亲传递给儿子数据,儿子触发一个父亲方法,将最 ...
- vue父子组件之间的通信
利用props在子组件接受父组件传过来的值1.父组件parentComp.vue <template> <childComp :fromParentToChild="fro ...
- Vue.js组件之间的调用
index.html: <div id="app"></div> 运行完index.html之后自动寻找运行main.js文件 main.js: impor ...
随机推荐
- express4.x的使用
①.安装 npm install -g express ②.创建应用 express [目录] 会在目录下生成 node_modules, 存放所有的项目依赖库.(每个项目管理自己的依赖,与Ma ...
- 北漂面试经历(一(两)年工作经验)-- Java基础部分
Java基础部分 常量和变量的区别:final 关键词修饰的变量是恒定不变的,如果还有static关键词修饰的话,常常称为编译期常量.变量,运行时可以修改其引用. Java基本类似有哪些 1 Byte ...
- Linux下Hadoop2.7.1集群环境的搭建(超详细版)
本文旨在提供最基本的,可以用于在生产环境进行Hadoop.HDFS分布式环境的搭建,对自己是个总结和整理,也能方便新人学习使用. 一.基础环境 ...
- Java设计模式随笔
大家都知道Java23种设计模式,大神总结如下: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...
- OC中@class的使用
作用: 可以简单地引用一个类简单使用@class Dog; //类的引入 #import "Dog.h"仅仅是告诉编译器:Dog是一个类;并不会包含Dog这个类的所有内容 具体使用 ...
- Telegram学习解析系列(一):认识一下Telegram的源码
前言: Telegram不知道有多少同行听过这玩意,或者在看它的源码.我是出于工作原因才接触到这东西,看的真是的......变方了!一个月估计刚刚找到门,还没进去多深,把自己的心得和对源码的认识以及我 ...
- React入门---组件嵌套-5
组件嵌套 我们现在需要组件嵌套,所以要创建其他组件,目前有一个头部组件,在./components/header.js; 接下来在components文件中创建:底部组件footer.js 和主体组件 ...
- 每天一道Java题[5]
题目 String.StringBuilder.StringBuffer有什么异同? 解答 相同点:String.StringBuilder.StringBuffer都可以用来存储字符串. 不同点: ...
- 使用函数指针调用C++虚函数
基本概念: 1. 函数指针,一个地址指针变量,其值指向代码区的某个函数首地址. 2. 虚函数,可以被子类覆写的C++成员函数.由虚函数表实现. 3. 虚函数表指针(vpt),指向虚函数表首地址的指针, ...
- VR市场爆炸-VR全景智慧城市
随着VR的火爆,越来越多的企业开始关注这种高新技术,也有越来越多VR虚拟现实公司应运而生,但是VR虚拟现实公司真的那么好做吗?虽然VR虚拟现实拥有巨大的市场潜力,但是同时它也非常烧钱,如果VR虚拟现实 ...