一步步快速掌握Vue组件间通信,看这篇文章就够了!

在 Vue 中,每一个组件都是独立的一个存在,如果需要组合使用这些组件,必然会将组件间的数据相互关联在一起,这种就叫做组件间的通信。例如: 有两个组件(A,B),在 B 组件中需要使用A 组件的数据,又或者在 A 组件中需要用到 B 组件的数据,类似于这种场景就会涉及到组件间的通信,也可以叫做数据传递!

创建组件

要想做通信这个事情,首先要有两个组件,下来我们就创建一下吧,后续内容我们会在此基础上进行修改。

首先我们需要创建一个 Vue 实例,可以把这个实例当做是 父组件

<div id="#app"></div>

<script>
	const vm = new Vue({
	  el: '#app'
	})
</script>

然后我们还需要一个子组件,这里就创建一个全局组件吧!需要注意的是 组件必须在Vue实例前面定义
第一个参数是组件的名字,第二个参数是组件的一些配置

第一个参数可以把模板直接写在组件内部,也可以写在组件的外部。
第二个参数中,可以配置像 Vue 实例中的属性,例如: data、methods、生命周期钩子函数....

Vue.component("component-child", {
  template: `<h2>This is child component...</h2>`,
  // data 、 methods、 生命周期钩子函数...都可以在组件中定义
})

// Vue 实例...

模板写在组件外部

<script type="text/template" id="childTemplate">
	// 注意: 在这里需要包一层 `div` 组件中只允许有一个根节点.
	<div>
		// 这里面写组件的内容
	<div>
</script>

<!-- 使用组件模板时直接把 id 献上就可以啦 -->
<script>
Vue.component("component-child", {
  template: '#childTemplate'
  // data 、 methods、 生命周期钩子函数...都可以在组件中定义
})
</script>

使用组件就非常简单了,直接在 html 中使用即可.

<div id="#app">
	<!-- 使用组件 -->
	<component-child></component-child>
</div>

需要用的组件就创建完了,接下来我们可以在浏览器中看一哈我们创建的组件吧!

组件通信有以下几种方式

一、props、$emit

使用 props 可以在子组件里接收父组件传递过来的数据

我们先在 Vue 实例 也就是 父组件 中定义一些数据

const vm = new Vue({
  el: '#app',
  // 以下是新增的内容
  data: {
    user: {  // 用户信息
      username: 'xiaosai',
      age: 18
    },
    cities: ['北京', '上海', '广州', '深圳']  // 城市列表
  }
})

要把数据传递给子组件,需要在组件上通过标签属性的方式传递,修改标签如下

扫描二维码关注公众号,回复: 10492757 查看本文章
<div id="app">
  <!-- 这里 v-bind 可以省略,简写成 : -->
  <component-child v-bind:user="user" v-bind:cities="cities"></component-child>
</div>

在这里插入图片描述

修改子组件,在子组件中定义 props 属性,用来接收父组件传递过来的数据

Vue.component("component-child", {
  props: ['user', 'cities'],  // 这是新增的内容,这里的值要和上图中的属性一致哦~这样才能拿到数据
  template: `<h2>This is child component...</h2>`,
})

拿到数据后就可以随意使用啦~~~~ 修改子组件模板,把数据渲染出来

Vue.component("component-child", {
  props: ['user', 'cities'],
  template: `
  <div>
    <p>用户信息: {{user.username}} 今年 {{user.age}} 岁</p>
    <ul>
      <li v-for="(c,index) in cities" :key="index">{{c}}</li>
    </ul>
  </div>
  `,
})

结果

使用 $emit 这种方式可以把子组件中的数据传递到父组件中.

我们已经知道了怎么从子组件中获取父组件的数据,那么如何在父组件中获取和使用子组件的数据呢,这时候我们可以使用 $emit 来做.

$emit 实际上是 Vue 实例的一个方法,它可以向外派发一个事件,把数据绑定到这个事件上。通过在标签上监听这个事件来获取数据。

Vue.component("component-child", {
  template: `
  	// 当点击按钮的时候,通过 $emit 派发一个自定义事件 my-event,把 data 中的 msg 数据传递出去
    <button @click="$emit('my-event',msg)">clickMe</button>
  `,
  data() {
    return {
      msg: 'this is child message....'
    }
  }
})

然后我们可以在组件上 监听这个自定义事件

<div id="app">
  <component-child @my-event="eventHandler"></component-child>
</div>


在Vue实例的 methods 中定义这个函数

const vm = new Vue({
  el: '#app',
  data: {
    childMessage: ''
  },
  methods: {
    eventHandler(data) {
      // 可以先打印一下 data 看看
      console.log('data: ', data)
   	  // 这里的 data 就是从子组件中传递出来的 数据,把他绑定到父组件中
      this.childMessage = data;
    }
  }
})

然后我们就可以在父组件的任意位置用这个数据啦~

<div id="app">
  <p>这条数据是从子组件中传递出来的: {{childMessage}}</p>
  <component-child @my-event="eventHandler"></component-child>
</div>

缺点

props、$emit 这种方式有一个缺点,例如说组件的嵌套层次深,这种通信方式就会显得相当麻烦,需要一层一层的去传递,非常不利于维护。

二、直接访问跟实例、组件实例

为了解决以上残留的问题,我们可以使用 $root 直接访问根实例、使用 $parent 访问父组件的实例、使用ref 访问子组件的实例. 我们还是用上面的组件案例来演示。

$root的用法

修改 Vue 实例 父组件

const vm = new Vue({
  el: '#app',
  data: {
    username: 'xiaosai'
  }
})

修改 子组件,在子组件中可以直接通过 $root 访问跟实例的数据, 不管嵌套多少层.都是访问的 Vue 实例的数据

Vue.component("component-child", {
  template: `
    <p>这里是子组件中,调用跟实例的数据  {{$root.username}}</p>
  `,
})

修改 html 为最原始的样子

<div id="app">
  <component-child></component-child>
</div>

运行的结果,可以顺利拿到 跟实例的数据
在这里插入图片描述
$parent 的用法

当然在这个例子中,跟实例就是 Vue 实例,也是一个父组件.

我们可以稍微修改一下代码,把$root 修改为 $parent ,运行结果在这个例子里是一样的。

Vue.component("component-child", {
  template: `
    <p>这里是子组件中,调用跟实例的数据  {{$parent.username}}</p>
  `,
})

由此可以看出 $root 是访问跟实例的,而 $parent 只能访问父组件(实例)。

访问子组件实例

在 Vue 中,我们也可以通过给组件设置 ref 属性,通过 $refs 访问子组件的实例,来看一哈我是怎么操作的吧~

修改子组件中的代码

Vue.component("component-child", {
  template: `
    <p>这里是子组件</p>
  `,
  data() {
    return {
      childMessage: 'this is child component data...'
    }
  }
})

需要在组件上定义一个 ref 属性,可以根据自己的喜欢任意命名

<div id="app">
  <component-child ref="childData"></component-child>
</div>

在父组件中可以通过$refs.childData 获取子组件实例

const vm = new Vue({
  el: '#app',
  mounted() {
    // 注意: $refs.childData 获取的是 子组件的实例,而不是data中的数据,还需要 .childMessage 才可以拿到数据
    console.log(this.$refs.childData.childMessage);
  }
})

注意:通过 $root$parent$refs访问的是实例本身,而 !不! 是! data中的数据,我们可以获取实例里面有的任何属性,例如 datamethod computed 等等

缺点

实际上直接访问实例的这种方式也是有缺点的,就是在子组件中可以修改父组件中的数据,这是很致命的,如果遇到什么 bug,很难找出问题所在。所以 Vue 官方也是不推荐使用这种方式进行通讯,当然对于比较小型的应用来说这种方式确实是非常简单方便的。

三、可以通过依赖注入的方式进行通信

在上述第二种方式中,我们虽然可以访问到父组件的内容,但是如果嵌套的层次过多,例如有10个嵌套,我总不能 通过 $parent 一层一层的的获取把,这是很不现实的。我们需要一种方式,来解决这个问题,这就是依赖注入。

在 实例中,有两个新的选项 provideinject , 现在我们去修改一下父组件代码

const vm = new Vue({
  el: '#app',
  provide() {
    return {
      msg: '这是需要通讯的数据'
    }
  }
})

provide 是一个函数,它返回一个数据对象,在 子组件 中我们需要像定义 props一样定义一个 inject ,也是一个数组,需要注意的是, inject 中的内容需要和 provide 中的 key 一致.

Vue.component("component-child", {
  inject: ['msg'],
  template: `
    <p>子组件:{{msg}}</p>
  `,
})

在这里插入图片描述
运行结果

缺点

使用 provide inject 这种方式通信的数据不是响应式的!!! 不是响应式的!!!

四、使用事件总线 EventBus 进行通信

可以使用一个空的 Vue 实例作为一个中间者,向组件来监听和派发事件,从而达到 数据通信的效果!

先创建一个空的 Vue 实例
const eventBus = new Vue()

在 #app 中添加一个按钮,并且绑定一个事件

<div id="app">
  <component-child></component-child>
  <button @click="clickHandler">clickMe</button>
</div>

在父组件中定义 clickHandler 方法

const vm = new Vue({
  el: '#app',
  data: {
    username: 'xiaosai'
  },
  methods: {
    clickHandler() {
      // 调用 eventBus 作为一个中间者去抛发一个事件 myEvent ,并且携带 username 作为数据传递过去
      eventBus.$emit('myEvent', this.username)
    }
  }
})

在子组件mounted生命周期钩子函数中监听事件

Vue.component("component-child", {
  template: `
    <p>子组件:{{msg}}</p>     <!-- 使用数据 -->
  `,
  data() {
    return {
      msg: ''
    }
  },
  mounted() {
  	// 通过 eventBus 的 $on 函数监听事件,并且拿到数据
    eventBus.$on('myEvent', data => {
   	  // 把取到的数据赋值给 data 中
      this.msg = data;
    })
  }
})

点击按钮查看运行结果
在这里插入图片描述

缺点

操作的数据依旧不是响应式的!!!


结尾总结

除了上述几种方式以外,还可以通过 Vuex 来管理数据,对于某些应用来说 Vue 官方也推荐使用这种方式,Vuex 也不是没有缺点的,后续会出一篇文章单独总结一下Vuex。但是也不是说上面几种方法都用不到,根据实际的业务场景去使用对应的方式才是王道!

发布了19 篇原创文章 · 获赞 13 · 访问量 5062

猜你喜欢

转载自blog.csdn.net/actionActivity/article/details/105275957