子父组件通信
props
父组件将一个函数作为 props 传递给子组件,子组件接收后调用该函数将想要传递的数据以实参的形式传递。
<!-- 准备一个容器 -->
<div id="root">
爸爸的小金库有{{money}}元 <br>
<Son :give="addMoney"></Son>
</div>
<template id="son">
<div>
<button @click="giveMoney">给爸爸发{{HongBao}}红包</button>
</div>
</template>
<script type="text/javascript">
const Son = Vue.extend({
template: '#son',
data() {
return {
HongBao: 888,
}
},
props: ['give'],
methods: {
giveMoney() {
this.give(this.HongBao);
}
}
})
// 创建vue实例
new Vue({
el: '#root',
data: {
money: 1000,
},
methods: {
addMoney(money) {
this.money += money;
}
},
components: {
Son,
}
})
</script>
自定义事件
1. 一种组件间的通信方式,适用于 子组件 ===> 父组件
2. 使用场景:A是父组件,B是子组件,B想给A传递数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
3. 绑定自定义事件:<br /> 1. 第一种方式:在父组件中` <Son @=myEventName = "fn" / >`<br /> 2. 第二种方式:在父组件中:
<Son ref = "sonCV" />
....
mounted(){
this.$refs.sonCV.$on('myEventName',this.fn);
}
3. 若想让自定义事件只触发一次,可以使用`once` 修饰符,或者使用 `$once` 方法。
4. 触发自定义事件:子组件中 `this.$emit('myEventName', ...数据)`
5. 解除自定义事件 `this.$off('myEventName')`
6. 组件上可以绑定原生DOM事件,需要使用 native 修饰符。这是因为组件上的事件会被认为是自定义事件 如 `<Son @click='fn' />` 会以为是名为click的自定义事件。
7. 注意:通过 this.$refs.xxx.$on('myEventName', fn) 绑定自定义事件时,回调要么配置在 methods 中,要么使用箭头函数,否则this指向会出现问题!!
<!-- 准备一个容器 -->
<div id="root">
爸爸的小金库有{{money}}元 <br>
<!-- 方法一 -->
儿子1<Son @give-hongbao="addMoney"></Son>
<!-- 方法二 -->
儿子2<Son ref="sonRef"></Son>
</div>
<template id="son">
<div>
<button @click="giveMoney">给爸爸发{{HongBao}}红包</button>
<button @click="relieve">解除自定义事件</button>
</div>
</template>
<script type="text/javascript">
const Son = Vue.extend({
template: '#son',
data() {
return {
HongBao: 888,
}
},
methods: {
giveMoney() {
console.log('我要触发自定义事件');
this.$emit('give-hongbao', this.HongBao);
},
relieve() {
// this.$off(); // 解除所有自定义事件
// this.$off(['give-hongbao']); // 解除多个自定义事件
this.$off('give-hongbao'); // 解除一个自定义事件
console.error('解除自定义事件');
}
}
})
// 创建vue实例
new Vue({
el: '#root',
data: {
money: 1000,
},
// 方法一:使用methods
methods: {
addMoney(sonHongBao) {
console.log('%c 自定义事件被触发了 ', 'background-color:green;');
this.money += sonHongBao;
},
},
// 方法二:使用ref
mounted() {
this.$refs.sonRef.$on('give-hongbao', (sonHongbao) => {
this.money += sonHongbao;
})
},
components: {
Son,
}
})
</script>
兄弟组件通信
当我们要把 Header 组件中的数据 传递到 List 组件中时,我们可以使用 全局事件总线、消息订阅与发布、VUEX 等。
但是我们此处要学的是最初级的做法。状态上升,使下组件无状态化。
人话就是:
因为 Header 和 LIst 组件现在无法正常沟通,我们就把他们的数据给到共同的父组件 APP 。
APP通过创建一个方法 methods
,把方法通过 props 传递给子组件 Header,Header 通过调用该方法,获取父组件中的参数。
隔代组件通信 $attrs
思路:把爷爷组件需要传递的数据通过 props 给到 Father 组件,Father 不声明 props 进行接收,而是给 Father 组件内部的 Grandson 组件添加v-bind="$attrs"
,然后 Grandson 组件就可以通过 this.$attrs.propsName。
关于 vm.$attrs vue官网的介绍:
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件——在创建高级别的组件时非常有用。
<div id="root">
<Father :toGrandsonHongbao="HongBao"></Father>
<button @click="giveMoney">爷爷给孙子发{{HongBao}}红包</button>
</div>
<!-- 父亲组件 -->
<template id="father">
<div>
<Grandson v-bind="$attrs"></Grandson>
</div>
</template>
<!-- 孙子组件 -->
<template id="grandson">
<div>
孙子有{{money}}元,和爷爷给的{{$attrs.tograndsonhongbao}}
</div>
</template>
const Grandson = Vue.extend({
template: '#grandson',
// // 默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉
inheritAttrs: false,
data() {
return {
money: 10,
}
},
})
const Father = Vue.extend({
template: '#father',
components: {
Grandson,
},
})
// 创建vue实例
new Vue({
el: '#root',
data: {
HongBao: 0,
},
methods: {
giveMoney() {
this.HongBao = 1000;
}
},
components: {
Father,
},
})
provide/inject
Vue2.2.0新增API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。
provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
全局事件总线 GlobalEventBus
EventBus 是一种组件间的通信方式,适用于任意组件间通信,是一种 发布——订阅的设计模式。
全局事件总线操作流程
new Vue({
...
beforeCreate(){
Vue.prototype.$bus = this;
},
})
this.$bus.$on('myEvent', (data) => { ... });
this.$bus.$emit('myEvent', this.data);
由于自定义事件是绑定给 $bus 身上的,事件不会随着组件的销毁而自动解绑,因此需要在绑定事件的组件上进行解绑
this.$bus.off('myEvent');
为什么可行?
- Vue 原型对象上包含事件处理的方法
$on(eventName, listener)
绑定自定义事件监听$emit(eventName, data)
分发自定义事件$off(eventName)
解绑自定义事件监听$once(eventName, listener)
绑定自定义事件监听,但只能处理一次
所有组件实例对象的原型对象的隐式原型属性 指向 Vue 的原型对象
VueComponent.prototype.proto === Vue.prototype
所有组件实例对象都能访问到Vue原型对象上的属性和方法。
Vue.prototype.bus = new Vue()
,所有组件实例对象都可以访问到 bus 这个属性。
<!-- 准备一个容器 -->
<div id="root">
爸爸的小金库有{{money}}元 <br>
<eldest-son></eldest-son>
<youngest-son></youngest-son>
</div>
<template id="eldestSon">
<div>
<button @click="giveMoney">给弟弟发{{HongBao}}红包</button>
</div>
</template>
<template id="youngestSon">
<div>
小儿子有{{money}}元
</div>
</template>
<script type="text/javascript">
const eldestSon = Vue.extend({
template: '#eldestSon',
data() {
return {
HongBao: 200,
}
},
methods: {
giveMoney() {
this.$EventBus.$emit('give-youngestSon', this.HongBao);
}
},
})
const youngestSon = Vue.extend({
template: '#youngestSon',
data() {
return {
money: 100,
}
},
mounted() {
this.$EventBus.$on('give-youngestSon', (money) => {
this.money += money;
})
},
beforeDestroy() {
this.$EventBus.$off('give-youngestSon');
},
})
// 创建vue实例
const vm = new Vue({
el: '#root',
data: {
money: 1000,
},
beforeCreate() {
Vue.prototype.$EventBus = this; // 安装全局事件总线,所有的组件对象都可以看到 $EventBus 这个属性
// Vue.prototype.$EventBus = new Vue(); // 安装全局事件总线,所有的组件对象都可以看到 $EventBus 这个属性
},
components: {
'eldest-son': eldestSon,
'youngest-son': youngestSon,
},
mounted() {
},
})
</script>
消息订阅与发布 pubsub
一种组件间通信的方式,适用于任意组件间通信。
基于 pubsub-js
实现
安装:pubsub-js
npm install —save pubsub-js
引入:PubSub
接收消息和发送消息的组件都需要引入这个库!!
import PubSub from 'pubsub-js';
console.log(PubSub);
接收数据的组件需要订阅消息
mounted() {
// 挂载完毕,订阅一个名为myMsg消息,类似js中的定时器会返回一个id,用这个id取消订阅。
this.pubId = PubSub.subscribe('myMsg', (msgName, data) => {
......
// msgName是消息名 即myMsg
// data 是传递来的数据
})
},
发送数据的组件需要发布消息
PubSub.publish('myMsg', this.data);
接收数据的组件销毁后需要取消订阅
beforeDestroy() { // 组件销毁前阶段 取消订阅 pubId 这个消息
PubSub.unsubscribe(this.pubId);
},
- 使用消息订阅与发布实现兄弟组件传参(大儿子给小儿子包红包) ```html <!DOCTYPE html>
```javascript
console.log(PubSub);
const eldestSon = Vue.extend({
template: '#eldestSon',
data() {
return {
HongBao: 200,
}
},
methods: {
giveMoney() {
// 发布消息
PubSub.publish('myMsg', this.HongBao);
}
},
})
const youngestSon = Vue.extend({
template: '#youngestSon',
data() {
return {
money: 100,
}
},
mounted() {
// 挂载完毕,订阅一个名为myMsg消息,类似js中的定时器会返回一个id,用这个id取消订阅。
this.pubId = PubSub.subscribe('myMsg', (msgName, data) => {
console.log('youngestSon订阅的myMsg被发布了', msgName, data);
this.money += data;
})
},
beforeDestroy() {
PubSub.unsubscribe(this.pubId);
},
})
// 创建vue实例
const vm = new Vue({
el: '#root',
data: {
money: 1000,
},
components: {
'eldest-son': eldestSon,
'youngest-son': youngestSon,
},
})