子父组件通信

props

父组件将一个函数作为 props 传递给子组件,子组件接收后调用该函数将想要传递的数据以实参的形式传递。

  1. <!-- 准备一个容器 -->
  2. <div id="root">
  3. 爸爸的小金库有{{money}}元 <br>
  4. <Son :give="addMoney"></Son>
  5. </div>
  6. <template id="son">
  7. <div>
  8. <button @click="giveMoney">给爸爸发{{HongBao}}红包</button>
  9. </div>
  10. </template>
  11. <script type="text/javascript">
  12. const Son = Vue.extend({
  13. template: '#son',
  14. data() {
  15. return {
  16. HongBao: 888,
  17. }
  18. },
  19. props: ['give'],
  20. methods: {
  21. giveMoney() {
  22. this.give(this.HongBao);
  23. }
  24. }
  25. })
  26. // 创建vue实例
  27. new Vue({
  28. el: '#root',
  29. data: {
  30. money: 1000,
  31. },
  32. methods: {
  33. addMoney(money) {
  34. this.money += money;
  35. }
  36. },
  37. components: {
  38. Son,
  39. }
  40. })
  41. </script>

自定义事件

  1. 1. 一种组件间的通信方式,适用于 子组件 ===> 父组件
  2. 2. 使用场景:A是父组件,B是子组件,B想给A传递数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
  3. 3. 绑定自定义事件:<br /> 1. 第一种方式:在父组件中` <Son @=myEventName = "fn" / >`<br /> 2. 第二种方式:在父组件中:
  1. <Son ref = "sonCV" />
  2. ....
  3. mounted(){
  4. this.$refs.sonCV.$on('myEventName',this.fn);
  5. }
  1. 3. 若想让自定义事件只触发一次,可以使用`once` 修饰符,或者使用 `$once` 方法。
  2. 4. 触发自定义事件:子组件中 `this.$emit('myEventName', ...数据)`
  3. 5. 解除自定义事件 `this.$off('myEventName')`
  4. 6. 组件上可以绑定原生DOM事件,需要使用 native 修饰符。这是因为组件上的事件会被认为是自定义事件 `<Son @click='fn' />` 会以为是名为click的自定义事件。
  5. 7. 注意:通过 this.$refs.xxx.$on('myEventName', fn) 绑定自定义事件时,回调要么配置在 methods 中,要么使用箭头函数,否则this指向会出现问题!!
  1. <!-- 准备一个容器 -->
  2. <div id="root">
  3. 爸爸的小金库有{{money}}元 <br>
  4. <!-- 方法一 -->
  5. 儿子1<Son @give-hongbao="addMoney"></Son>
  6. <!-- 方法二 -->
  7. 儿子2<Son ref="sonRef"></Son>
  8. </div>
  9. <template id="son">
  10. <div>
  11. <button @click="giveMoney">给爸爸发{{HongBao}}红包</button>
  12. <button @click="relieve">解除自定义事件</button>
  13. </div>
  14. </template>
  15. <script type="text/javascript">
  16. const Son = Vue.extend({
  17. template: '#son',
  18. data() {
  19. return {
  20. HongBao: 888,
  21. }
  22. },
  23. methods: {
  24. giveMoney() {
  25. console.log('我要触发自定义事件');
  26. this.$emit('give-hongbao', this.HongBao);
  27. },
  28. relieve() {
  29. // this.$off(); // 解除所有自定义事件
  30. // this.$off(['give-hongbao']); // 解除多个自定义事件
  31. this.$off('give-hongbao'); // 解除一个自定义事件
  32. console.error('解除自定义事件');
  33. }
  34. }
  35. })
  36. // 创建vue实例
  37. new Vue({
  38. el: '#root',
  39. data: {
  40. money: 1000,
  41. },
  42. // 方法一:使用methods
  43. methods: {
  44. addMoney(sonHongBao) {
  45. console.log('%c 自定义事件被触发了 ', 'background-color:green;');
  46. this.money += sonHongBao;
  47. },
  48. },
  49. // 方法二:使用ref
  50. mounted() {
  51. this.$refs.sonRef.$on('give-hongbao', (sonHongbao) => {
  52. this.money += sonHongbao;
  53. })
  54. },
  55. components: {
  56. Son,
  57. }
  58. })
  59. </script>

兄弟组件通信

image.png
当我们要把 Header 组件中的数据 传递到 List 组件中时,我们可以使用 全局事件总线、消息订阅与发布、VUEX 等。
但是我们此处要学的是最初级的做法。状态上升,使下组件无状态化。
人话就是:
因为 Header 和 LIst 组件现在无法正常沟通,我们就把他们的数据给到共同的父组件 APP 。
image.png
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” 传入内部组件——在创建高级别的组件时非常有用。

  1. <div id="root">
  2. <Father :toGrandsonHongbao="HongBao"></Father>
  3. <button @click="giveMoney">爷爷给孙子发{{HongBao}}红包</button>
  4. </div>
  5. <!-- 父亲组件 -->
  6. <template id="father">
  7. <div>
  8. <Grandson v-bind="$attrs"></Grandson>
  9. </div>
  10. </template>
  11. <!-- 孙子组件 -->
  12. <template id="grandson">
  13. <div>
  14. 孙子有{{money}}元,和爷爷给的{{$attrs.tograndsonhongbao}}
  15. </div>
  16. </template>
  1. const Grandson = Vue.extend({
  2. template: '#grandson',
  3. // // 默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉
  4. inheritAttrs: false,
  5. data() {
  6. return {
  7. money: 10,
  8. }
  9. },
  10. })
  11. const Father = Vue.extend({
  12. template: '#father',
  13. components: {
  14. Grandson,
  15. },
  16. })
  17. // 创建vue实例
  18. new Vue({
  19. el: '#root',
  20. data: {
  21. HongBao: 0,
  22. },
  23. methods: {
  24. giveMoney() {
  25. this.HongBao = 1000;
  26. }
  27. },
  28. components: {
  29. Father,
  30. },
  31. })

provide/inject

Vue2.2.0新增API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。
provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

  1. // 父级组件提供 'foo'
  2. var Provider = {
  3. provide: {
  4. foo: 'bar'
  5. },
  6. // ...
  7. }
  8. // 子组件注入 'foo'
  9. var Child = {
  10. inject: ['foo'],
  11. created () {
  12. console.log(this.foo) // => "bar"
  13. }
  14. // ...
  15. }

全局事件总线 GlobalEventBus

EventBus 是一种组件间的通信方式,适用于任意组件间通信,是一种 发布——订阅的设计模式。

image.png

全局事件总线操作流程

  1. new Vue({
  2. ...
  3. beforeCreate(){
  4. Vue.prototype.$bus = this;
  5. },
  6. })
  1. this.$bus.$on('myEvent', (data) => { ... });
  1. this.$bus.$emit('myEvent', this.data);

由于自定义事件是绑定给 $bus 身上的,事件不会随着组件的销毁而自动解绑,因此需要在绑定事件的组件上进行解绑

  1. this.$bus.off('myEvent');

为什么可行?

  1. Vue 原型对象上包含事件处理的方法

$on(eventName, listener) 绑定自定义事件监听
$emit(eventName, data) 分发自定义事件
$off(eventName) 解绑自定义事件监听
$once(eventName, listener) 绑定自定义事件监听,但只能处理一次

  1. 所有组件实例对象的原型对象的隐式原型属性 指向 Vue 的原型对象

    VueComponent.prototype.proto === Vue.prototype

  2. 所有组件实例对象都能访问到Vue原型对象上的属性和方法。

  3. Vue.prototype.bus = new Vue(),所有组件实例对象都可以访问到 bus 这个属性。
  1. <!-- 准备一个容器 -->
  2. <div id="root">
  3. 爸爸的小金库有{{money}}元 <br>
  4. <eldest-son></eldest-son>
  5. <youngest-son></youngest-son>
  6. </div>
  7. <template id="eldestSon">
  8. <div>
  9. <button @click="giveMoney">给弟弟发{{HongBao}}红包</button>
  10. </div>
  11. </template>
  12. <template id="youngestSon">
  13. <div>
  14. 小儿子有{{money}}元
  15. </div>
  16. </template>
  17. <script type="text/javascript">
  18. const eldestSon = Vue.extend({
  19. template: '#eldestSon',
  20. data() {
  21. return {
  22. HongBao: 200,
  23. }
  24. },
  25. methods: {
  26. giveMoney() {
  27. this.$EventBus.$emit('give-youngestSon', this.HongBao);
  28. }
  29. },
  30. })
  31. const youngestSon = Vue.extend({
  32. template: '#youngestSon',
  33. data() {
  34. return {
  35. money: 100,
  36. }
  37. },
  38. mounted() {
  39. this.$EventBus.$on('give-youngestSon', (money) => {
  40. this.money += money;
  41. })
  42. },
  43. beforeDestroy() {
  44. this.$EventBus.$off('give-youngestSon');
  45. },
  46. })
  47. // 创建vue实例
  48. const vm = new Vue({
  49. el: '#root',
  50. data: {
  51. money: 1000,
  52. },
  53. beforeCreate() {
  54. Vue.prototype.$EventBus = this; // 安装全局事件总线,所有的组件对象都可以看到 $EventBus 这个属性
  55. // Vue.prototype.$EventBus = new Vue(); // 安装全局事件总线,所有的组件对象都可以看到 $EventBus 这个属性
  56. },
  57. components: {
  58. 'eldest-son': eldestSon,
  59. 'youngest-son': youngestSon,
  60. },
  61. mounted() {
  62. },
  63. })
  64. </script>

消息订阅与发布 pubsub

一种组件间通信的方式,适用于任意组件间通信。
基于 pubsub-js 实现

  1. 安装:pubsub-js

    npm install —save pubsub-js

  2. 引入:PubSub

接收消息和发送消息的组件都需要引入这个库!!

  1. import PubSub from 'pubsub-js';
  2. console.log(PubSub);

image.png

  1. 接收数据的组件需要订阅消息

    1. mounted() {
    2. // 挂载完毕,订阅一个名为myMsg消息,类似js中的定时器会返回一个id,用这个id取消订阅。
    3. this.pubId = PubSub.subscribe('myMsg', (msgName, data) => {
    4. ......
    5. // msgName是消息名 即myMsg
    6. // data 是传递来的数据
    7. })
    8. },
  2. 发送数据的组件需要发布消息

    1. PubSub.publish('myMsg', this.data);
  3. 接收数据的组件销毁后需要取消订阅

    1. beforeDestroy() { // 组件销毁前阶段 取消订阅 pubId 这个消息
    2. PubSub.unsubscribe(this.pubId);
    3. },
  • 使用消息订阅与发布实现兄弟组件传参(大儿子给小儿子包红包) ```html <!DOCTYPE html>
爸爸的小金库有{{money}}元

  1. ```javascript
  2. console.log(PubSub);
  3. const eldestSon = Vue.extend({
  4. template: '#eldestSon',
  5. data() {
  6. return {
  7. HongBao: 200,
  8. }
  9. },
  10. methods: {
  11. giveMoney() {
  12. // 发布消息
  13. PubSub.publish('myMsg', this.HongBao);
  14. }
  15. },
  16. })
  17. const youngestSon = Vue.extend({
  18. template: '#youngestSon',
  19. data() {
  20. return {
  21. money: 100,
  22. }
  23. },
  24. mounted() {
  25. // 挂载完毕,订阅一个名为myMsg消息,类似js中的定时器会返回一个id,用这个id取消订阅。
  26. this.pubId = PubSub.subscribe('myMsg', (msgName, data) => {
  27. console.log('youngestSon订阅的myMsg被发布了', msgName, data);
  28. this.money += data;
  29. })
  30. },
  31. beforeDestroy() {
  32. PubSub.unsubscribe(this.pubId);
  33. },
  34. })
  35. // 创建vue实例
  36. const vm = new Vue({
  37. el: '#root',
  38. data: {
  39. money: 1000,
  40. },
  41. components: {
  42. 'eldest-son': eldestSon,
  43. 'youngest-son': youngestSon,
  44. },
  45. })

Vue 组件间通信六种方式(完整版)