Skip to content

七种组件通信

  1. props/$emit
  2. $parent/$children
  3. provide/inject
  4. ref
  5. eventBus
  6. Vuex
  7. $attrs/$listeners

一、props/$emit

父组件通过v-bind把值传给子组件,子组件通过props接收。子组件通过$emit触发父组件的自定义事件,父组件通过v-on监听子组件的自定义事件。
父组件:

vue
<template>
    <div>
        <children :list='list' @onEmit='onEmit'></children>
        <div>{{a}}</div>
    </div>
</template>
<script>
import children from './children'
export default {
    components:{
        children,
    },
    data(){
        return{
            list:[{id:1,name:'斗破'},{id:2,name:'圣墟'},{id:3,name:'牧神记'}],
            a:-1,
        }
    },
    methods:{
        onEmit(val){
            console.log(val)
            this.a = val;
        }
    }
}
</script>

子组件:

vue
<template>
    <div>
        <div v-for="(item,index) in list" :key="index" @click="emitClick(index)">{{item}}</div>
    </div>
</template>
<script>
export default {
    props:['list'],
    data(){
        return{

        }
    },
    methods:{
        emitClick(index){
            console.log(11,index)
            this.$emit('onEmit',index)
        }
    }
}
</script>

二、$parent/$children

父组件通过$parent获取子组件,子组件通过$children获取父组件。
父组件:

vue
<template>
    <div style="margin-top:100px">
        <children></children>
        <div>{{ message }}</div>
        <button @click="btn">点击获取子组件的值</button>
    </div>
</template>
<script>
import children from './children'
export default {
    components:{
        children,
    },
    data(){
        return{
            message:'来至父组件的消息'
        }
    },
    methods:{
        btn(){
            this.$children[0].message = this.message
        }
    }
}
</script>

子组件:

vue
<template>
    <div>
        <div>{{message}}</div>
        <div>获取父组件的值:{{parentM}}</div>
    </div>
</template>
<script>
export default {
    props:['list'],
    data(){
        return{
            message:'来至子组件的消息',
        }
    },
    computed:{
        parentM(){
            return this.$parent.message
        }
    },
    methods:{

    }
}
</script>

总结:以上只能在父子组件中传值,不能隔代传值,不能同级传值。

三、provide/ inject

provide和inject是vue2.0新增的api。父组件通过provide来进行定义变量,子组件通过inject来注入。不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据 代码示例: 存在三个vue文件,分别死a,b,c。b是a的子组件,c是b的子组件。
a组件:

vue
<template>
    <div>
        <b></b>
    </div>
</template>
<script>
import b from './b'
export default {
    components:{
        b,
    },
    provide:{
        message:'来至最大的消息'
    },
}
</script>

b组件:

vue
<template>
    <div>
        <div>b:{{messageB}}</div>
        <c></c>
    </div>
</template>
<script>
import c from './c'
export default {
    inject:['message'],
    data(){
        return{
            messageB:this.message,
        }
    },
    components:{
        c
    }
}
</script>

c组件:

vue
<template>
    <div>
        c:{{messageC}}
    </div>
</template>
<script>
export default {
    inject:['message'],
    data(){
        return{
            messageC:this.message
        }
    }
}
</script>

四、ref

ref如果在dom元素上使用,指向的就是dom元素。在子组件上使用,指向的就是组件实例。可以访问数据或者方法。
父组件通过ref声明子组件标识,通过this.$refs.子组件标识名来获取子组件实例。父组件引用子组件,如果子组件在父组件的循环中,父组件调用子组件方法时,需要用this.refs.子组件[0].方法, 进行调用方法 示例:

js
// 组件使用
<Upload :ref="`fileUpload${index}`" @findFileUrl="findFileUrl" :urlIndex="`${index}`" urlType="file"></Upload>
// 组件调用
this.$refs[`fileUpload${index}`][0].submitUpload()
// 模板使用
<div v-for="(item, index) in list" :key="index">
	<div  v-for="(value, key) in item.mediaMaterialList" :key="key" :ref="`list${index}`">
    		<div v-if="item.type == 'image'" :ref="`list1${key}`">

     		</div>
	</div>
</div>

// js获取
this.$refs[`list${index}`][key].scrollIntoView()

五、eventBus

eventBus中央事件总线,所有组件的事件中心,组件可以向中心注册发送事件和接收事件,组件可以和其他任何组件通信。通过bus.emit触发事件,bus.on监听触发的事件
代码示例: 需要新建一个事件总线并且导出, 新建js文件eventBus.js文件

js
import Vue from 'vue'
export default EventBus = new Vue()
vue
<template>
    <div style="margin-top:100px">
        <b></b>
        <c></c>
    </div>
</template>
<script>
import b from './b'
import c from './c'
export default {
    components:{
        b,
        c
    },
}
</script>

分别有两个组件b和c,b和c为兄弟组件。在b组件中发送事件,在c组件中接收。

vue
<template>
    <div>
        <button @click="clickB">b组件的点击事件{{num}}</button>
    </div>
</template>
<script>
import {EventBus} from './eventBus.js'
console.log(EventBus)
export default {
    data(){
        return{
            num:1,
        }
    },
    methods:{
        clickB(){
            EventBus.$emit('addition',{
                num:this.num++
            })
        }
    }
}
</script>
vue
<template>
    <div>
        c:{{count}}
    </div>
</template>
<script>
import {EventBus} from './eventBus.js'
export default {
    data(){
        return{
            count:0,
        }
    },
    mounted(){
        EventBus.$on('addition',param => {
            this.count = this.count + param.num;
        })
    }
}
</script>

移除事件监听:

js
import { eventBus } from 'event-bus.js'
EventBus.$off('addition', {})

六、vuex

1.vuex模块介绍

  1. state:存储数据
  2. mutations:修改state中的数据,不能处理异步事件。
  3. actions:提交mutation,而不直接更状态,可以包含异步操作
  4. getters:计算state中的数据,像vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据想关计算。
  5. modules:模块,用于将项目中各个模块的状态分开定义和操作,便于维护。

2.vuex创建

在main.js同级下新建store文件夹,中新建index.js,actions.js,getters.js,mutations.js,state.js文件
在main.js中引入: import store from './store'
在实例中添加store:

js
new Vue({
    el: '#app',
    router,
    store,
    components: { App },
    template: ''
})

index.js文件:

js
import Vue from "vue";
import Vuex from "vuex";
import state from "./state";
import mutations from "./mutations";
import * as actions from "./actions";
import * as getters from "./getters";

Vue.use(Vuex);

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

state.js文件:

js
const state = {
    token: "",
}
export default state

mutations.js文件:

js
const updateToken = (state, token) => {
    state.token = token;
}
export default { token }

actions.js文件:

js
// context对象会自动传入,它与store实例具有相同的方法和属性
const updateToken = (context, token) => {
    // 1. 发异步请求, 请求数据
    // 2. commit调用mutation来修改/保存数据
    // context.commit('mutation名', 载荷)
    context.commit('updateToken', token)
}

getters.js文件:

js
export const getToken = (state) => state.token

3.使用

js
import { mapActions. mapState } from 'vuex'

// 2中方式

// 方法一
this.$store.dispatch('updateToken', '1212312')
// 方法二
// 通过methods方法添加映射
methods: {
    ...mapActions([
        'updateToken'
    ])
}

// 2.使用
this.updateToken()

// 获取数据
computed: {
    ...mapState([
        'token'
    ])
    // 推荐写法
    ...mapState({
        token: state => state.token,
        test: test => state.test
    })
}

七、$attrs/$listeners

vue2.4中引用了$attrs和$listeners,新增了inheritAttrs选项。
inheritAttrs:默认为true。true的时候会把没有在props中声明的属性挂载到组件的根元素上。
listeners:官方文档解释:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="listeners" 传入内部组件——在创建更高层次的组件时非常有用。即子组件可以触发父组件的事件。
内部组件通过this.emit('方法名')去调用。 attrs:存放没有被子组件继承的的数据对象 父组件:

vue
<template>
    <div style="margin-top:80px">
        <test2 :name='name' :age='age' :gender='gender'
	 :height='height' :width='width' @event1='ev1' @event2='ev2'></test2>
    </div>
</template>
<script>
import test2 from './test2'
export default {
    data(){
        return{
            name:'sun',
            age:'18',
            gender:'男',
            height:'182',
            width:'1222',
        }
    },
    components:{
        test2,
    },
    methods:{
        ev1(){
            console.log('触发了方法1')
        },
        ev2(){
            console.log('触发了方法2')
        }
    }
}
</script>

子组件:

vue
<template>
    <div>
        name{{name}}
        <div>子组件一的$attrs{{$attrs}}</div>
        <test3 v-bind="$attrs" v-on="$listeners"></test3>
    </div>
</template>
<script>
import test3 from './test3'
export default {
    props:{
        name:String,//可以不写,直接冲attrs取
    },
    inheritAttrs: false,//关闭自动挂载在组件根元素上,没有在props中声明的属性
    components:{
        test3
    },
    created(){
        console.log('组件1'+this.$attrs)
    },
    mounted(){
        this.$emit('event1')
    }
}
</script>

test3代码:

vue
<template>
    <div>
        <!-- age{{age}} -->
        <div>子组件二的$attrs{{$attrs}}</div>
    </div>
</template>
<script>
export default {
    // props:{
    //     age:String,
    // },
    inheritAttrs: false,
    created(){
        console.log('组件2'+this.$attrs)
    },
    mounted(){
        this.$emit('event2')
    }
}
</script>

总结:

  • 父子组件通信:props; $parent / $children; provide / inject ; ref ; $attrs / $listeners
  • 兄弟组件通信:eventBus,vuex
  • 跨级通信:eventBus;Vuex;provide / inject 、$attrs / $listeners

Released under the MIT License.