从“$on”来看vue中的数据传递

通信

为什么要通信

由于vue将不同页面分发成不同组件进行开发,所以就不可避免的会遇到组件间的通信问题。
例如,有一个表格组件,和一个弹窗组件。逻辑是点击表格中的一个按钮,跳出弹窗显示该按钮所在行的数据。那如何将表格组件中的行数据,传递给弹窗组件呢?

组件通信,就是指组件与组件之间的数据传递

  1. 组件的数据是独立分散在不同文件中不同数据对象中的,无法直接访问其他组件的数据。
  2. 想使用其他组件的数据,就需要组件通信,让数据像水流一样进行传递

组件间的通信

一个组件,与其他组件的关系有三种情况

  1. 是其他组件的父组件
  2. 是其他组件的子组件
  3. 非父子关系,为兄弟组件

这里着重介绍非父子组件如何使用 “$on” 进行通信

非父子组件之间通信

可以想象,如果两个组件之间没有关系,那就可以手动为他们创造一条 “通道” 进行通信,数据可以在通道中传递。文章最后附上详细代码

创建通道

新建一个js文件,并在其中创建一个vue实例。该实例能被后续的接收方、发送方都能访问。那么该实例就如同一个通道一样,承载着双方的信息实例

// eventBus.js文件
import Vue from 'vue'
const Bus = new Vue()
export default Bus

创建接收方

创建testA.vue作为接收方。关键代码在于在组件创建后(“created”),使用“$on”来监听 Bus中的 “sendMsg” 的事件。 “sendMsg” 就像一位乘客,一但该乘客登上Bus这个通道,作为接收方的testA能立马监听到,并且执行一个对应的回调函数。

//testA.vue的实例内容
    created() {
      Bus.$on('sendMsg', (msg) => {
        // console.log(msg)
        this.msg = msg
      })
    }

创建发送方

创建testB.vue作为发送方。关键代码在于组件方法中,使用 “$emit” 方法来触发一个事件。这个过程就好像testB将 “sendMsg” 作为一个乘客送到传输通道Bus中。

//testB.vue的实例内容
methods: {
  sendMsgFn() {
    Bus.$emit('sendMsg', '测试发送')
  },
}

这里的 “$emit” 的第一个参数是事件名称,后续的、额外参数都会传递给事件监听器的回调函数。

interface ComponentPublicInstance {
  $emit(event: string, ...args: any[]): void
}
export default {
  created() {
    this.$emit('foo'); // 仅触发事件
    this.$emit('bar', 1, 2, 3);  // 带有额外的参数
  }
}

效果

实现的效果如图所示,点击B组件的按钮,A组件会收到来自B组件的信息
在这里插入图片描述

讨论

发布-订阅

这里的Vue的数据传递的方式,体现了“发布-订阅模型”的思想。A是订阅方,B是发布方。A通过 $on来订阅事件,B通过 $emit来订阅事件。

订阅者、发布者可以不唯一

这里只有A一个订阅者,现实可以多个订阅者,他们都可以监听到sendMsg这个事件,以及传递过来的数据。同理,发布者也可以不唯一,一个接收方可以监听多个发布者(通过写多个 $on语句来完成)。

缺陷

如果每个组件之间都通过一条通道来进行维护,可以想象当设计一个变量需要被多个组件访问、修改时,那么维护之间的通道会变的非常麻烦,不利于数据的共享。因此可以设计一个容器,这个容器里的数据对所有监听它的人开放,对数据修改会在全局起作用,以供对数据进行访问和修改,后续就使用Vuex和Pina来充当该类角色。

附测试代码

App.vue

<template>
  <div id="app">
    <div>
      <img alt="Vue logo" src="./assets/logo.png">
      <HelloWorld msg="Hello World!" />
    </div>
    <div>

      <div>
        <testA ></testA>
        <testC ></testC>
      </div>

      <testB style="margin: auto;"></testB>
    </div>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import testA from './components/testA.vue'
import testB from './components/testB.vue'
import testC from './components/testC.vue'

export default {
  name: 'App',
  components: {
    HelloWorld,
    testA,
    testB,
    testC,
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

eventBus.js

// eventBus.js文件
import Vue from 'vue'
const Bus = new Vue()
export default Bus

testA.vue

<template>
  <div class="base-a">
    <p>A组件(接收方)</p>
    <p>{{ msg }}</p>
    <button @click="sendMsgFn">发送消息</button>
  </div>
</template>

<script>
import Bus from '../eventBus'
export default {
  data() {
    return {
      msg: '',
    }
  },
  created() {
    Bus.$on('sendMsg', (msg) => {
      // console.log(msg)
      msg+="修改"
      this.msg = msg 
    })
  },
  methods: {
      sendMsgFn() {
        Bus.$emit('sendAnotherMsg', '测试发送另一条消息');
      },
    },
}
</script>

<style scoped>
.base-a {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
  justify-content: center;
}
</style>

testB.vue

<template>
    <div class="base-b">
      <div>B组件(发送方)</div>
      <button @click="sendMsgFn">发送消息</button>
    </div>
  </template>
  
  <script>
  import Bus from '../eventBus'
  export default {
    methods: {
      sendMsgFn() {
        Bus.$emit('sendMsg', '测试发送')
      },
    },
  }
  </script>
  
  <style scoped>
  .base-b {
    width: 200px;
    height: 200px;
    border: 3px solid #000;
    border-radius: 3px;
    margin: 10px;
  }
  </style>
  
  

testC.vue

<template>
    <div class="base-a">
      <p>C组件(接收方)</p>  
      <p>{{msg}}</p>  
      <p>{{AnotherMsg}}</p>  
    </div>
  </template>
  
  <script>
  import Bus from '../eventBus'
  export default {
    data() {
      return {
        msg: '',
        AnotherMsg: '',
      }
    },
    created() {
      Bus.$on('sendMsg', (msg) => {
        // console.log(msg)
        this.msg = msg
      });
      Bus.$on('sendAnotherMsg', (AnotherMsg) => {
        console.log(AnotherMsg);
        this.AnotherMsg = AnotherMsg
      });
    },
  }
  </script>
  
  <style scoped>
  .base-a {
    width: 200px;
    height: 200px;
    border: 3px solid #000;
    border-radius: 3px;
    margin: 10px;
  }
  </style>
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值