美文网首页
Vue组件通信方式盘点

Vue组件通信方式盘点

作者: 莫伊剑客 | 来源:发表于2020-11-18 08:53 被阅读0次

前言


组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件可以有以下几种关系:



如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。

针对不同的使用场景,如何选择行之有效的通信方式?这是我们所要探讨的主题。本文总结了vue组件间通信的几种方式,如props、

方法一:props/$emit


父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Vue组件通信方式盘点——props/$emit</title>
</head>

<body>
  <div id="app">
    <child :title='title' v-on:parent-on-click='parentClick'></child>
    <div v-if='childParams'>我是{{childParams}}传过来的参数!</div>
  </div>
  <script src="../js/vue.js"></script>
  <script>
    // 子组件
    Vue.component('child', {
      props: {
        title: String
      },
      data() {
        return {
          name: '子组件'
        }
      },
      template: `<div @click='onClick'>我是{{title}}传过来的参数!</div>`,
      methods: {
        onClick() {
          this.$emit('parent-on-click', this.name)
        }
      },
    })
    // 父亲组件
    new Vue({
      el: '#app',
      data() {
        return {
          title: '父组件',
          childParams: ''
        }
      },
      methods: {
        parentClick($event) {
          this.childParams = $event
        }
      },
    })
  </script>

</body>

</html>

方法二:eventbus


这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Vue组件通信方式盘点——$on/$emit</title>
</head>

<body>
  <div id="app">
    <div @click='onClickA'>A组件</div>
    <h3 v-if='params'>{{params}}</h3>
    <b-child></b-child>
  </div>
  <script src="../js/vue.js"></script>
  <script>
    class Bus {
      constructor() {
        this.callbacks = {}
      }
      $on(name, fn) {
        this.callbacks[name] = this.callbacks[name] || []
        this.callbacks[name].push(fn)
      }
      $emit(name, args) {
        if (this.callbacks[name]) {
          this.callbacks[name].forEach(cb => cb(args))
        }
      }
    }
    Vue.prototype.$bus = new Bus()
    // 或者
    // Vue.prototype.$bus=new Vue()
    // 子组件C
    Vue.component('c-child', {
      template: `
        <div>
          <div @click='onClick'>C组件</div>
        </div>
        `,
      methods: {
        onClick() {
          this.$bus.$emit('params', '我是C组件!')
          console.dir(this.$bus)
        }
      },
    })
    // 子组件D
    Vue.component('d-child', {
      template: `
        <div>
          <div>D组件</div>
          <h3 v-if='params'>{{params}}</h3>
        </div>
        `,
      data() {
        return {
          params: ''
        }
      },
      created() {
        this.$bus.$on('params', args => {
          this.params = args
        })
      },
    })
    // 子组件B
    Vue.component('b-child', {
      data() {
        return {
          params: ''
        }
      },
      template: `
        <div>
          <p>B组件</p> 
          <h3 v-if='params'>{{params}}</h3>
          <c-child></c-child>
          <d-child></d-child>
        </div>
      `,
      created() {
        this.$bus.$on('params', args => {
          console.log(args)
          this.params = args
        });
      },
      mounted() {
        // this.$bus.$on('params', args => {
        //   console.log(args)
        //   this.params = args
        // });
      },
    })
    // 父亲组件(A组件)
    new Vue({
      el: '#app',
      name: 'A',
      data() {
        return {
          params: ''
        }
      },
      created() {
        this.$bus.$on('params', args => {
          this.params = args
        });
      },
      methods: {
        onClickA() {
          this.$bus.$emit('params', '我是A组件')
          console.dir(this.$bus)
        }
      },
    })
  </script>

</body>

</html>

Vue.prototype.bus = new Bus()或Vue.prototype.bus = new Vue()都可以!

方法三:vuex


创建唯一的全局数据管理者store,通过它管理数据并通知组件状态变更。(此处略过,请查看官网)https://vuex.vuejs.org/zh/

方法四:$parent/$root


兄弟组件之间通信可通过共同祖辈搭桥,$parent或$root

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Vue组件通信方式盘点——$parent/$children/ref</title>
</head>

<body>
  <div id="app">
    <div>A组件</div>
    <b-child></b-child>
  </div>
  <script src="../js/vue.js"></script>
  <script>
    // 子组件C
    Vue.component('c-child', {
      template: `
        <div>
          <div>C组件</div>
          <button @click='onClick'>通过$parent更改组件状态</button>
          <button @click='withRoot'>通过$root更改组件状态</button>
          <h3 v-if='params'>{{params}}</h3>
        </div>
        `,
      data() {
        return {
          params: ''
        }
      },
      methods: {
        onClick() {
          this.$parent.$emit('params','C组件通过'+this.$parent.name+'组件')
        },
        withRoot() {
          this.$root.$emit('params','C组件通过'+this.$root.name+'组件')
        }
      },
    })
    // 子组件D
    Vue.component('d-child', {
      template: `
        <div>
          <div>D组件</div>
          <h3 v-if='params'>{{params}}</h3>
        </div>
        `,
      data() {
        return {
          params: ''
        }
      },
      created () {
        this.$parent.$on('params',args=>{
          this.params=args+'操作了D组件!'
        });
        this.$root.$on('params',args=>{
          this.params=args+'操作了D组件!'
        });
      },
    })
    // 子组件B
    Vue.component('b-child', {
      data() {
        return {
          name: 'B'
        }
      },
      template: `
        <div>
          <p>B组件</p> 
          <c-child></c-child>
          <d-child></d-child>
        </div>
      `,
    })
    // 父亲组件(A组件)
    new Vue({
      el: '#app',
      data() {
        return {
          name: 'root'
        }
      },
    })
  </script>

</body>

</html>

方法五:$children/ref


父组件可以通过$children/ref访问子组件实现父子通信。

// parent  $children
this.$children[0].xx = 'xxx'

// parent $refs
<HelloWorld ref="hw"/> 
mounted() { 
    this.$refs.hw.xx = 'xxx'
 }

注意:$children不能保证子元素顺序

方法六:$attrs/$listeners


包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。当一个组件没有
声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外),并且可以通过 v- bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Vue组件通信方式盘点——$attrs/$listeners</title>
</head>

<body>
  <div id="app">
    <div>A组件</div>
    <b-child :data='data' @click='cb'></b-child>
    <h3>{{cbData}}</h3>
  </div>
  <script src="../js/vue.js"></script>
  <script>
    // 子组件B
    Vue.component('b-child', {
      template: `
        <div>
          <p>B组件</p> 
          <p>$attrs:{{$attrs}}</p>
          <button @click='$listeners.click("子页面被点击啦!")'>回调父级事件</button>
        </div>
      `,
      created() {
        console.dir(this.$listeners);
      },
    })
    // 父亲组件(A组件)
    new Vue({
      el: '#app',
      data() {
        return {
          data: 'root',
          cbData: ''
        }
      },
      methods: {
        cb($event) {
          console.log($event)
          this.cbData = $event
        }
      },
    })
  </script>

</body>

</html>

方法七:provide/inject


能够实现祖先和后代之间传值

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Vue组件通信方式盘点——provide/inject</title>
</head>

<body>
  <div id="app">
    <div>A组件</div>
    <b-child></b-child>
  </div>
  <script src="../js/vue.js"></script>
  <script>
    // 子组件B
    Vue.component('b-child', {
      template: `
        <div>
          <p>B组件</p> 
          <p>{{parentName}}</p>
        </div>
      `,
      inject:['parentName']
    })
    // 父亲组件(A组件)
    new Vue({
      el: '#app',
      provide(){
        return {
          parentName:'A页面'
        }
      }
    })
  </script>

</body>

</html>

总结


常见使用场景可以分为三类:

父子通信: 父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent/$children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners

兄弟之间通信: $bus、Vuex

跨级通信:
$attrs/$listeners,$bus, Vuex, provide/inject,

相关文章

  • vue | 组件通信方式盘点

    父组件 => 子组件 属性 props 特性 $attrs 引用 refs 子元素$children 子组件 =>...

  • Vue组件通信方式盘点

    前言 组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引...

  • Vue相关知识点

    1、vue父子组件之间的通信 在vue组件通信中其中最常见通信方式就是父子组件之中的通性,而父子组件的设定方式在不...

  • vue组件之间通信

    vue 组件之间通信 vue组件之间通信方式: 1.父组件通过props向下传数据给子组件,子组件通过$emit事...

  • Vue3 的 7 种和 Vue2 的 12 种组件通信

    Vue2.x组件通信12种方式写在后面了,先来 Vue3 的 Vue3 组件通信方式 props $emit ex...

  • vue 组件通信方式 ,父子、隔代、兄弟 三类通信,六种方法

    Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面分别介绍每种通信方式且会说...

  • Vue组件通信的几种方式【转】

    Vue组件通信的几种方式【转】 组件通信主要有以下几种方式:props,$emit和$on,vuex,$attrs...

  • Vue组件通信方式

    本文主要介绍关于Vue组件通信的四种方式,分别是父向子组件通信、子向父组件通信、非父子组件的EventBus通信、...

  • Vuex(一) —— 集中式的状态管理仓库

    目录 Vue组件间通信方式回顾组件内的状态管理流程组件间通信方式父组件给子组件传值 (最简单的一种方式)子组件给父...

  • vue Bus总线

    vue中我们经常遇到组件之间的通信,通常的通信方式有 父 → 子、子 → 父、兄弟组件 之间的通信。通常处理方式...

网友评论

      本文标题:Vue组件通信方式盘点

      本文链接:https://www.haomeiwen.com/subject/afatiktx.html