Vue组件通信方式合集

42 阅读3分钟

前言

之前学习组件间通信,网上直接来一大堆,什么子父组件通信、兄弟组件通信、祖孙组件通信等,接着直接列举八种方法,一一去学吧。但是我感觉学完之后老是忘记。之后通过看教程,查阅文档,我用了一种独特的方式去学习组件间通信,我个人感觉通过这种方式,我还是记得比较清晰的,特意来给大家分享一下。

主要思想是这样:你站在当前组件的角度,去向父、祖、子、其它组件传值和取值。

Vue组件通信方式


首先还是简单介绍一下组件通信的几种方式吧,主要分为以下几种(还是以当前组件为视角):
  • 当前组件与父组件、子组件通信(也就是父子组件通信)。
  • 当前组件与祖组件通信(祖孙组件通信)。
  • 当前组件与兄弟组件通信(兄弟组件通信)。
  • 当前组件与无关系组件通信(非关系型组件通信)

可以抽象为一幅图。可能有些难懂,后续会配合代码做具体讲解

image.png

总结为:站在你当前组件的视角,你如何向父、祖、子、其它组件要值和传值。


1、当前组件->父组件

1.1、$emit方式

Father组件传值,需要在Current组件通过$emit()将数据传递过去,$emit()接受两个参数(this.$emit('transMessage', this.transval));

  • 第一个参数为事件名,这个值要和Father组件中绑定在Current组件上的名字对应<Current @transMessage="setdata"></Current>@transMessage)。
  • 第二个参数是传递到父组件的数据。

为了节省空间,省略了所有样式代码

Curret组件代码

<template>
  <div>
    <div class="cur">
        <h2>Current组件,传递值为{{transval}}</h2>
        <el-button @click="transData">传值</el-button>
    </div>
  </div>
</template>
<script>
export default {
  data() {
      return {
          nema:'Current',
          transval:'hello,i am current'
      };
  },
  methods: {   
        transData:function(){
            // 点击'传值'按钮后,会触发trans这个自定义事件,
            this.$emit('transMessage', this.transval);
        }
    },
};
</script>

Father组件代码

<template>
    <div>
        <div class="father">
            <h2>Father,即将接收值为{{val}}</h2>
        </div>
        <div>
            <Current @transMessage="setdata"></Current>
        </div>
        
    </div>
</template>
<script>
import Current from './Current.vue';
export default {
    components:{
        Current
    },
    data() {
        return {
            val:'Father'
        };
    },
    methods:{
        setdata(par){
            console.log(par);
            this.val = par
        }
    }
};
</script>

运行实例: 点击传值之后,Father组件接收到Current组件transval值。

1.1.gif

1.2、$refs方式

Current组件上添加ref属性,通过$refs取到Current组件这个实例对象,从而取到Current组件中的transval值。

Father组件代码

<template>
    <div>
        <div class="father">
            <h2>Father,接收值为{{val}}</h2>
        </div>
        <div>
            <Current ref="Curall"></Current>
        </div>
        
    </div>
</template>
<script>
import Current from './Current.vue';
export default {
    components:{
        Current
    },
    data() {
        return {
            val:null
        };
    },
    mounted() {
        this.val = this.$refs.Curall.transval
    },
};
</script>

Current组件代码

<template>
  <div>
    <div class="cur">
        <h2>Current组件,传递值为{{transval}}</h2>
    </div>
  </div>
</template>
<script>
export default {
  data() {
      return {
          nema:'Current',
          transval:'hello,i am current'
      };
  }
};
</script>

运行实例:

image.png

1.3 $children方式

Current组件中,通过$children获取到Child组件,之后可以获取到Child组件中的数据。这个方式和ref方式类似,都是取到了Child这个组件。

Current组件代码

<template>
  <div>
    <div class="cur">
        <h2>Current组件,Child组件中的val值为{{transval}}</h2>
    </div>
    <div>
        <Child></Child>
    </div>
  </div>
</template>
<script>
import Child from "./Child.vue"
export default {
  components:{
    Child
    },
    nema:'Current',
    data() {
        return {
            nema:'Current',
            transval:null
        }
    },
    mounted(){
      // 用这个方式需要注意子组件的顺序问题
      this.transval = this.$children[0].val
    }
};
</script>

Child组件代码

<template>
    <div>
        <div class="father">
            <h2>这是Child组件</h2>
        </div>
    </div>
</template>
<script>
export default {
    data() {
        return {
            val:"hello,i am Child"
        };
    },
};
</script>

运行实例:

image.png

2、当前组件->子组件

2.1、pop方式

通过将Current组件中的数据直接绑定到Child组件上,Child组件组件内通过prop来获取数据。

Current组件代码

<template>
  <div>
    <div class="cur">
        <h2>Current组件,传递值为{{transval}}</h2>
    </div>
    <div>
      <Child :info="transval"></Child>
    </div>
  </div>
</template>
<script>
import Child from "./Child.vue"
export default {
  components:{
    Child
    },
  data() {
      return {
          nema:'Current',
          transval:'hello,i am current'
      };
  }
};
</script>

Child组件代码

<template>
    <div>
        <div class="father">
            <h2>Child组件,接收值为{{info}}</h2>
        </div>
    </div>
</template>
<script>
export default {
    data() {
        return {
            val:null
        };
    },
    props:{
        info:String
    },
};
</script>

运行实例:

image.png

2.2 $parent方式

Child组件中,通过$parent获取到Current组件,之后可以获取到Current组件中的数据。

Current组件代码

<template>
  <div>
    <div class="cur">
        <h2>Current组件</h2>
    </div>
    <div>
        <Child></Child>
    </div>
  </div>
</template>
<script>
import Child from "./Child.vue"
export default {
  components:{
    Child
    },
    nema:'Current',
    data() {
        return {
            nema:'Current',
            transval:"hello,i am Current"
        }
    },
    
};
</script>

Child组件代码

<template>
    <div>
        <div class="father">
            <h2>这是Child组件,从Current获取的值为{{val}}</h2>
        </div>
    </div>
</template>
<script>
export default {
    data() {
        return {
            val:null
        };
    },
    mounted(){
        this.val = this.$parent.transval
    }
};
</script>

运行实例:

image.png

2.3 provide + inject方式

此方式是通过在Current组件中添加provide配置项将数据传递出去,在Child组件中添加inject接收传递来的数据。

Current组件代码

<template>
  <div>
    <div class="cur">
        <h2>Current组件</h2>
    </div>
    <div>
        <Child></Child>
    </div>
  </div>
</template>
<script>
import Child from "./Child.vue"
export default {
  components:{Child},
    nema:'Current',
    data() {
        return {
            nema:'Current',
            transval:"hello,i am Current"
        }
    },
    provide:{
        "transval":"hello,i am Current"
    }
};
</script>

Child组件代码

<template>
    <div>
        <div class="father">
            <h2>这是Child组件,从Current获取的值为{{transval}}</h2>
        </div>
    </div>
</template>
<script>
export default {
    inject:["transval"]
};
</script>

运行实例:

image.png

3、当前组件->祖组件

3.1、$listeners方法

这里有三个组件,分别为Grandparent组件Parent组件Current组件、嵌套关系如下:Grandparent组件中包含Parent组件Parent组件包含Current组件

Parent组件添加v-on="$listeners"。将Current组件的事件传递到Grandparent组件$listeners相当于一个中间容器。

Grandparent组件代码

<template>
    <div>
      <div class="gra">
          <h2>Grandparent组件,从Current组件获取的值为{{getdata}}</h2>
      </div>
      <Parent @transdata="getData"></Parent>
    </div>
  </template>
  <script>
  import Parent from './Parent.vue';
  export default {
      nema:'Grandparent',
      components:{Parent},
      data() {
          return {
              nema:'Grandparent',
              getdata:null
          }
      },
      methods:{
        getData(par){
            console.log("here");
            this.getdata = par
        }
      }
  };
  </script>

Parent组件代码

<template>
    <div>
      <div class="par">
          <h2>Parent组件</h2>
      </div>
      <Current v-on="$listeners"></Current>
    </div>
</template>
<script>
import Current from './Current.vue';
export default {
    nema:'parent',
    components:{Current},
    data() {
        return {
            nema:'parent',
        }
    }
};
</script>

Current组件代码

<template>
    <div>
      <div class="cur">
          <h2>Current组件</h2>
          <el-button @click="transData">传值</el-button>
      </div>
    </div>
</template>
<script>
export default {
    nema:'Current',
    data() {
        return {
            nema:'Current',
            transdata:"hello,i am Current"
        }
    },
    methods:{
    transData(){
        this.$emit('transdata',this.transdata)
    }
    }
};
</script>

运行实例:

1.2.gif

4、当前组件->孙子组件

4.1、$attrs方式

有三个组件,分别为Current组件Child组件Grandson组件、嵌套关系如下:Current组件中包含Child组件Child组件包含Grandson组件

Current组件中将数据传出去(<Child :info="transdata"></Child>,将Current组件中的transdata数据以info传出去),但是在Child组件中不用:info接收,而是采用v-bind="$attrs"这种数据继续向下传递。因此可在``Grandson组件直接使用$attrs.info`获取数据

Current组件`代码

<template>
    <div>
        <div class="parent">
            <h2>Parent组件</h2>
        </div>
        <div>
            <Current v-bind="$attrs"></Current>
        </div>
    </div>
</template>
<script>
import Current from "./Current"
export default {
    components:{Current},
    nema:'Current',
      data() {
          return {
              nema:'Parent',
          }
      },
};
</script>

Child组件代码

<template>
    <div>
      <div class="gra">
          <h2>Grandparent组件</h2>
      </div>
      <div>
          <Parent :info="transdata"></Parent>
      </div>
    </div>
  </template>
  <script>
  import Parent from "./Parent.vue"
  export default {
    components:{Parent},
      nema:'Grandparent',
      data() {
          return {
              nema:'Grandparent',
              transdata:"hello,i am Grandparent"
          }
      },
  };
  </script>

Grandson组件代码

<template>
    <div>
      <div class="cur">
          <h2>Current组件,从Grandparent获得的值为{{$attrs.info}}</h2>
      </div>
    </div>
  </template>
  <script>
  export default {
      nema:'Current',
      data() {
          return {
              nema:'Current',
          }
      }
  };
  </script>

运行实例:

image.png

4.2、provide + inject方式

provide + inject方式在前面用来父子之间传值,当然,它也可以用来祖孙之间传值。在Current组件加入provide属性,在Grandson组件中加入inject属性

Current组件代码

<template>
    <div>
      <div class="cur">
          <h2>Current组件</h2>
      </div>
      <div>
          <Child></Child>
      </div>
    </div>
  </template>
  <script>
  import Child from "./Child.vue"
  export default {
    components:{Child},
      nema:'Grandparent',
      data() {
          return {
              nema:'Current组件'
          }
      },
      provide:{
          "transval":"hello,i am Current组件"
      }
  };
  </script>

Child组件代码

<template>
    <div>
        <div class="child">
            <h2>Child组件</h2>
        </div>
        <div>
            <Grandson></Grandson>
        </div>
    </div>
</template>
<script>
import Grandson from "./Grandson"
export default {
    components:{Grandson},
    nema:'Current',
      data() {
          return {
              nema:'Child',
          }
      },
};
</script>

Grandson组件代码

<template>
    <div>
      <div class="grandson">
          <h2>Grandson组件,从Current获得的值为{{transval}}</h2>
      </div>
    </div>
  </template>
  <script>
  export default {
      nema:'Current',
      data() {
          return {
              nema:'Current',
          }
      },
      inject:["transval"]
      
  };
  </script>

运行实例:

image.png

5、当前组件->兄弟组件

5.1 $emit + props方式。如果有三个组件,Parent组件Current(也是Child1)组件Child2组件,包含关系如下代码所示:

<Parent>
    <Current></Current>
    <Child2></Child2>
</Parent>

具体流程是Child2组件通过$emit方式传值给Parent组件,之后Parent组件通过props方式,改变Child2中的数据,这种方式是间接的。

Parent组件代码

<template>
    <div>
      <div class="par">
          <h2>Parent组件</h2>
          <div class="box">
            <Current :info="val"></Current>
            <Child2 @transdata="getdata"></Child2>
          </div>
      </div>
    </div>
  </template>
  <script>
  import Current from "./Current.vue"
  import Child2 from "./Child2.vue"
  export default {
    components:{Current,Child2},
      nema:'Parent组件',
      data() {
          return {
              nema:'Parent组件',
              val:null
          }
      },
    methods:{
        getdata(par){
            console.log("hello");
            this.val = par
        }
    }
  };
  </script>

Current组件代码

<template>

    <div class="cur">
        <h2>Curent组件{{info}}</h2>
    </div>
</template>
<script>
export default {
    nema:'parent',
    props:{
        info:String
    },
    data() {
        return {
            nema:'parent',
        }
    }
};
</script>

Child2组件代码

<template>
    <div class="child2">
        <h2>Child2组件</h2>
        <el-button @click="transData">传值</el-button>
    </div>

</template>
<script>
export default {
    nema:'Current',
    data() {
        return {
            nema:'Current',
            transval:"hello,i am Child2"
        }
    },
    methods:{
    transData(){
            this.$emit('transdata',this.transval)
        }
    }
};
</script>

运行实例:

1.3.gif

5.2、eventBus方式

主要思想就是创建一个空的组件,利用这个组件做中间件,在传值的组件中,用$emit()发射事件,在接受值的组件中,用$on()接受事件。

Parent组件代码

<template>
    <div>
      <div class="par">
          <h2>Parent组件</h2>
          <div class="box">
            <Current></Current>
            <Child2></Child2>
          </div>
      </div>
    </div>
</template>
<script>
import Current from "./Current.vue"
import Child2 from "./Child2.vue"
export default {
  components:{Current,Child2},
    nema:'Parent组件',
};
</script>

Current组件代码

<template>
    <div class="cur">
        <h2>Curent组件,从Child2获取的值为{{val}}</h2>
    </div>
</template>
<script>
import {eventBus} from "../../main.js"
export default {
    nema:'parent',
    data() {
        return {
            nema:'parent',
            val : null
        }
    },
    mounted(){
        eventBus.$on("transdata",(par)=>{
            this.val = par
        })
    }
};
</script>

Child2组件代码

<template>
    <div class="child2">
        <h2>Child2组件</h2>
        <el-button @click="transData">传值</el-button>
    </div>

</template>
<script>
import {eventBus} from "../../main.js"
export default {
    nema:'Current',
    data() {
        return {
            nema:'Current',
            transval:"hello,i am Child2"
        }
    },
    methods:{
    transData(){
        eventBus.$emit('transdata',this.transval)
        }
    }
};
</script>

运行实例:

1.4.gif

6、无关系组件通信

6.1 也可以用事件总线eventBus,详情参照5.2

6.2 vuex

vuex的使用场景一般是需要管理大量共享数据,这需要对自己的项目做出分析、权衡。如果您不打算开发大型单页应用,使用Vuex可能是繁琐冗余的。

vuex使用步骤如下:在main.js中导入并使用store实例

  1. 首先下载vuex(npm install vuex),这里需要注意版本问题,原因大致是这样的。2022年2月7日,vue3就成为了默认版本。并且vue3成为默认版本的同时,vuex也更新到了4版本。也就是,现在如果执行npm i vuex,安装的就是vuex4了。而vuex的4版本只能在vue3中使用。如果我们非要在vue2的项目当中使用vuex的4版本,就会出现如上图所示的报错。解决办法参照这篇文章:vuex报错解决办法:
  2. 初始化配置vuex:
import Vue from "vue";
import Vuex from "vuex"

Vue.use(Vuex)

const store =  new Vuex.Store({
    state:{
        val:"hello,i am vuex data"
    },
    mutations:{
        changeVal(state){
            state.val = "hello,i am vuex data(changed)"
        }
    }
})

export default store
  1. 在main.js中导入并使用store实例
import store from './store'

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>',
  store
})
  1. 使用

此实例包含三个组件,Parent组件Currnt组件Other组件Parent组件包含Currnt组件Other组件(这里的包含关系只是为了显示方便,实际操作不涉及任何父子组件传值)。

Parent组件代码

<template>
    <div>
      <div class="par">
          <h2>Parent组件</h2>
          <div class="box">
            <Current></Current>
            <Child2></Child2>
          </div>
      </div>
    </div>
</template>
<script>
import Current from "./Current.vue"
import Child2 from "./Child2.vue"
export default {
  components:{Current,Child2},
    nema:'Parent组件',
};
</script>

Currnt组件代码

<template>

    <div class="cur">
        <h2>Curent组件,vuex中val值为{{$store.state.val}}</h2>
    </div>
</template>
<script>
export default {
    nema:'Current'
};
</script>

Other组件代码

<template>
    <div class="other">
        <h2>Other组件</h2>
        <el-button @click="changeData">change</el-button>
    </div>

</template>
<script>
export default {
    nema:'other',
    methods:{
        changeData(){
            this.$store.commit("changeVal")
        }
    }
};
</script>

运行实例:

1.5.gif

7、总结

总结一下就是:

  • 当前组件传值给父组件($emit、父组件通过$refs、父组件通过$children
  • 当前组件传值给子组件(prop、子组件通过$parent、### eventBus父组件provide数据+子组件inject数据
  • 当前组件传值给祖组件($listeners
  • 当前组件传值给孙子组件(祖组件provide数据+孙子组件inject数据
  • 当前组件传值给兄弟组件($emit + props方式、eventBus
  • 当前组件传值给无关系型组件(vuex

以上就是vue组件的常用通信方式,具体使用哪一种我们可以根据实际场景灵活使用。