1. 90前端首页
  2. 前端框架

彻底搞懂Vue组件间通信的6种方式(建议收藏 !)

在vue的学习中,vue组件间的通信是不得不了解的,在实际开发中,也是非常常用的,所以这里我总结了Vue组件的通信的6种方式,希望能帮助小伙伴们更好更快的去理解Vue组件间的通信

组件关系说明

彻底搞懂Vue组件间通信的6种方式(建议收藏 !)

由上边的图可以看出以下几个关系:

  • A与B是父子关系
  • A与C是父子关系
  • B与D是父子关系
  • B与E是父子关系
  • B与C是兄弟关系
  • D与E是堂兄关系
  • A与D是隔代关系
  • A与E是隔代关系

由上边几个关系对下边场景预览:

  • 父子 组件之间的数据传递
  • 兄弟 组件之间的数据传递
  • 祖先组件 与 子组件 之间的数据传递

下面来为大家详细讲解实现这些关系的几种通信方式

props/$emit

$parent / $children与 ref

$emit/$on

vuex

$attrs/$listeners

provide/inject

几种组件通信方法更好地选用

当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

节制地使用$parent和$children,它们的主要目的是作为访问组件的应急方法,
更推荐用props和events实现父子组件通信

如果仅仅是传递数据,就用$attrs/$listeners好点
如何不仅传递数据,还做中间处理,就用vuex好些

方法一:props/$emit

  • 父组件A 向 子组件B 传递数据 通过props的方法
  • 子组件B 向 父组件A 发送数据 通过emit

父组件向子组件传递数据

下面通过一个例子来看父组件是如何向子组件传递数据的:
这个例子是子组件son.vue 通过 props 获取 父组件father.vue 中的数据:
sonList: [\'小白\', \'小红\', \'小蓝\',\'小绿\']

  • father父组件
<template>
  <div class=\"father\">
    <com-son :sons=\"sonList\"></com-son>
  </div>
</template>

<script>
import comSon from \'./son\'
export default {
  name: \'HelloWorld\',
  components: { comSon },
  data() {
    return {
      sonList: [\'小白\', \'小红\', \'小蓝\',\'小绿\']
    }
  }
}
</script>
  • 子组件 son.vue
<template>
  <div>
    <span v-for=\"(item, index) in sons\" :key=\"index\">{{item}}</span>
  </div>
</template>

<script>
export default {
  props: [\'sons\']
}
</script>

注意:

props 只可以从上一级组件传递到下一级组件,也就是父子组件,即这就是单向数据流

props是只读,不可以被修改,所有被修改都会失效和被警告

子组件向父组件传递数据

下面通过一个例子来看子组件是如何向父组件传递值:这个例子是子组件son.vue通过$emit向父组件值的传递

  • 父组件father.vue
<template>
  <div class=\"father\">
    <com-son :sons=\"sonList\" @onEmitIndex=\"onEmitIndex\"></com-son>
    <p>{{currentIndex}}</p>
  </div>
</template>

<script>
import comSon from \'./son\'
export default {
  name: \'HelloWorld\',
  components: { comSon },
  data() {
    return {
      currentIndex: -1,
      sonList: [\'小白\', \'小红\', \'小蓝\',\'小绿\']
    }
  },
  methods:{
    onEmitIndex(idx){
      this.currentIndex = idx
    }
  }
}
</script>
  • 子组件son.vue
<template>
  <div>
    <span v-for=\"(item, index) in sons\" :key=\"index\" @click=\"emitIndex(index)\">{{item}}</span>
  </div>
</template>

<script>
export default {
  props: [\'sons\'],
  methods: {
    emitIndex(index){
      this.$emit(\'onEmitIndex\',index)
    }
  }
}
</script>

方法二:$parent/$children与ref

  • 子实例可以用this.$parent访问父实例
  • 子实例被推入父实例的$children
  • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例

在这里我要说一下:

节制地使用$parent和$children,它们的主要目的是作为访问组件的应急方法,

更推荐用props和events实现父子组件通信

下面我们来通过一个实例说明$parent和$children的用法:

  • 父组件father.vue
<template>
  <div class=\"father\">
    <com-son></com-son>
    <button @click=\"name\">点击改变子组件的值</button>
  </div>
</template>

<script>
import comSon from \'./son\'
export default {
  name: \'HelloWorld\',
  components: { comSon },
  data() {
    return {
      msg: \'hello,早上好!\'
    }
  },
  methods:{
    name(){
      this.$children[0].message = \"hello\"
    }
  }
}
</script>
  • 子组件son.vue
<template>
  <div class=\"com_a\">
    <span>{{message}}</span>
    <p>获取父组件的值:{{parentVal}}</p>
  </div>
</template>

<script>
export default {
  data(){
    return {
      message:\'Good wether\'
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}
</script>

下边再说ref访问组件的例子:

  • 子组件
export default {
  data () {
    return {
      title: \'Vue.js\'
    }
  },
  methods: {
    sayHello () {
      window.alert(\'Hello\');
    }
  }
}
  • 父组件
<template>
  <component-a ref=\"comA\"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.title);
      comA.sayHello();
    }
  }
</script>

注意:

这两种方法的弊端,无法在跨域兄弟间通信

$emit/$on

这个方法可用于父子、隔代、兄弟组件通信

这种方式是通过一个类似App.vue的实例作为一个模块的事件中心,用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件

当项目比较大的时候,可以选择更好的状态管理解决方案vuex

下边我们来通过一个实例说明emit/on的用法:

在这里先说一下:

有两个组件A、B,在B组件中接收到A组件传过来的数据

首先开辟个新的Vue根实例

然后我们在A组件中通过$emit方式去定义一个自定义事件方法

然后通过$on去接收A组件自定义的事件传过来的值

  • 首先创建一个vue的空白实例
import Vue from \'vue\'
export default new Vue()
  • 子组件A
把组件A通过$emit传到那个Vue空白实例里面
<template>
    <div>
        <span>A组件->{{msg}}</span>
        <input type=\"button\" value=\"把a组件数据传给b\" @click =\"send\">
    </div>
</template>
<script>
import vmson from \"util/emptyVue\"
export default {
    data(){
        return {
            msg:{
                a:\'666\',
                b:\'999\'
            }
        }
    },
    methods:{
        send:function(){
            vmson.$emit(\"aevent\",this.msg)
        }
    }
}
</script>
  • 子组件B
组件B通过$on去监听vmson实例中的自定义方法aevent
<template>
 <div>
    <span>{{msg}}</span>
 </div>
</template>
<script>
      import vmson from \"util/emptyVue\"
      export default {
         data(){
                return {
                    msg:\"\"
                }
            },
         mounted(){
                vmson.$on(\"aevent\",(val)=>{//监听aevent事件
                    console.log(val);//打印出来结果
                    this.msg = val;
                })
          }
    }
</script>
  • 父组件
这个父组件就是把A、B两个组件放在父组件中注册渲染
<template>
     <div>
      <childa></childa>
      <childb></childb>    
     </div>
</template>
<script>
   import childa from \'./childa.vue\';
   import childb from \'./childb.vue\';
   export default {
    components:{
        childa,
        childb
    },
    data(){
        return {
            msg:\"\"
        }
    },
    methods:{
       
    }
   }
</script>

Vuex

Vuex是什么?

Vuex是一个专为Vue.js应用程序开发的状态管理模式,它解决了组件之间同一状态的共享问题,它采用集中式存储管理应用的所有组件的状态,所以组件就可以和stort通讯了,其实Vuex就是用来管理组件之间通信的一个组件

为什么要使用Vuex?

假如不使用vuex

  • 父子组件依赖同一个state

彻底搞懂Vue组件间通信的6种方式(建议收藏 !)

  • 兄弟组件依赖同一个state

彻底搞懂Vue组件间通信的6种方式(建议收藏 !)

用了Vuex之后
彻底搞懂Vue组件间通信的6种方式(建议收藏 !)

Vuex各个模块

state

state中存放页面共享的状态字段

getters

相当于当前模块state的计算属性

mutations

如果想更新state中的字段,提交mutations中定义的事件的唯一的方式
(key为事件名,value是一个函数),但是中国事件函数必须是同步执行的

actions

可以定义异步函数,并在回调中提交mutation,就相当于异步更新了state中的字段

modules

类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护

Vuex实例应用

  • 父组件
<template>
  <div id=\"app\">
    <ChildA/>
    <ChildB/>
  </div>
</template>

<script>
  import ChildA from \'components/ChildA\'
  import ChildB from \'components/ChildB\'

  export default {
    name: \'App\',
    components: {ChildA, ChildB}
  }
</script>
  • 子组件ChildA
 <template>
  <div id=\"childA\">
    <h1>我是A组件</h1>
    <button @click=\"transform\">点我让B组件接收到数据</button>
    <p>{BMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        AMessage: \'Hello,B组件,我是A组件\'
      }
    },
    computed: {
      BMessage() {
        return this.$store.state.BMsg
      }
    },
    methods: {
      transform() {
        this.$store.commit(\'receiveAMsg\', {
          AMsg: this.AMessage
        })
      }
    }
  }
</script>
  • 子组件ChildB
<template>
  <div id=\"childB\">
    <h1>我是B组件</h1>
    <button @click=\"transform\">点我看A组件接收的数据</button>
    <p>{{AMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        BMessage: \'Hello,A组件,我是B组件\'
      }
    },
    computed: {
      AMessage() {
        return this.$store.state.AMsg
      }
    },
    methods: {
      transform() {
        this.$store.commit(\'receiveBMsg\', {
          BMsg: this.BMessage
        })
      }
    }
  }
</script>
  • vuex模块store.js
import Vue from \'vue\'
import Vuex from \'vuex\'

Vue.use(Vuex)

const state = {
  // 初始化A和B组件的数据,等待获取
  AMsg: \'\',
  BMsg: \'\'
}

const mutations = {
  receiveAMsg(state, payload) {
    state.AMsg = payload.AMsg
  },
  receiveBMsg(state, payload) {
    state.BMsg = payload.BMsg
  }
}

export default new Vuex.Store({
  state,
  mutations
})

$attrs/$listeners

用在父组件传递数据给子组件或者孙组件

如果仅仅是传递数据,就用$attrs/$listeners好点

如何不仅传递数据,还做中间处理,就用vuex好些

$attrs:

$attrs继承所有的父组件属性(除了prop传递的属性、class和style)

当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind=\"$attrs\" 传入内部组件。通常配合 inheritAttrs 选项一起使用。

$listeners:

它是一个对象,包含了父作用域中的v-on事件监听器,可以配合v-on=\"$listeners\"将所有的事件监听器指向这个组件的某个特定的子元素

下面我们来通过一个实例看$attrs/$listeners的用法:

例:假设有三个组件:A组件包含B组件,B组件包含C组件

  • A组件
<template>
    <div id=\"app\">
        <child1 :p-child1=\"child1\" :p-child2=\"child2\" v-on:test1=\"onTest1\" 
        v-on:test2=\"onTest2\"
        //此处监听了两个事件,可以在B组件或者C组件中直接触发
        >
        </child1>
    </div>
</template>
<script>
import Child1 from \'./Child1.vue\';
export default {
    data() {
        return {};
    },
    components: { Child1 },
    methods: {
        onTest1() {
            console.log(\'test1 running...\');
        },
        onTest2() {
            console.log(\'test2 running\');
        }
    }
};
</script>
  • B组件
<template>
    <div class=\"child-1\">
        <p>in child1:</p>
        <p>props: {{pChild1}}</p>
        <p>$attrs: {{$attrs}}</p>
        <hr>
        <child2 v-bind=\"$attrs\" v-on=\"$listeners\"></child2>
            //v-on绑定了$listeners,所以C组件能直接触发test
            //v-bind绑定了$attrs,所以C组件可以获取A组件传递下来的props的值
    </div>
</template>
<script>
import Child2 from \'./Child2.vue\';
export default {
    props: [\'pChild1\'],
    data() {
        return {};
    },
    inheritAttrs: false,
    components: { Child2 },
    mounted() {
        this.$emit(\'test1\');
    }
};
</script>
  • C组件
<template>
    <div class=\"child-2\">
        <p>in child2:</p>
        <p>props: {{pChild2}}</p>
        <p>$attrs: {{$attrs}}</p>
        <hr>
    </div>
</template>
<script>
export default {
    props: [\'pChild2\'],
    data() {
        return {};
    },
    inheritAttrs: false,
    mounted() {
        this.$emit(\'test2\');
    }
};
</script>

provide/inject

祖先组件中通过provider来提供变量,然后在孙组件中通过inject来注入变量

procide/inject API主要解决了跨域组件间的通讯问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系

下边通过一个例子来说明provide/inject的用法:

  • 父组件
<template>
    <div>
        <son prop=\"data\"></son>
    </div>
</template>
 
<script>
export default {
    provide: {
        name: \'Tom\'
    }
}
  • 孙子组件

这里的孙子组件指的是:父组件、子组件、孙子组件

<template>
    <div>
        {{name}}
    </div>
</template>
 
<script>
export default {
    name: \'grandson\',
    inject: [name]
}
</script>

这里可以通过inject直接访问其两个层次以上的数据,

用法与props完全相同

总结

vue组件间的通讯大致可以分为三类

父子通讯

props/emit、parent/children、 attrs/$listeners、provide/inject API、ref

父向子传递数据通过props

子向父传递是通过$emit、event

子实例访问父实例通过$parent

父实例访问子实例通过$children

$attrs用父组件传递数据给子组件或孙组件
(包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外))

listeners用父组件传递数据给子组件或孙组件
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器

祖先组件通过provider提供变量给子孙组件

子孙组件通过inject注入变量给祖先组件

ref用来访问组件实例

兄弟通信

Vuex

vuex用来作为兄弟之间和跨级之间的通信

跨级通信

Vuex、attrs/listeners、provide/inject API

vuex用来作为兄弟之间和跨级之间的通信

$attrs用父组件传递数据给子组件或孙组件
(包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外))

listeners用父组件传递数据给子组件或孙组件
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器

祖先组件通过provider提供变量给子孙组件

子孙组件通过inject注入变量给祖先组件

最后

如果本文对你有帮助得话,给本文点个赞❤️❤️❤️

本文来自网络整理,转载请注明原出处:https://segmentfault.com/a/1190000022083517

展开阅读全文

发表评论

登录后才能评论