Vue 组件的通信方式都有哪些?

说一下 Vue 组件的通信方式都有哪些?(父子组件,兄弟组件,多级嵌套组件等等)

  • 一、父组件向子组件传值
  • 二、子组件向父组件传值
  • 三、兄弟组件传值
  • 四、跨组件

 

一、父组件向子组件传值

1.1props方式:可以是数组或对象,用于接收父组件的数据

<div id="app">
    <child-component :msg="message" :count="count"></child-component>
    <button @click="count++">
            点击+1
    </button>
</div>
<script>
    const childComponent = {
        props: {
            msg: String
            count: Number
        },
        template: `<div><p>{{msg}}</p><p>{{count}}</p><div>`
    }
    new Vue({
        el: '#app',
        data: {
            message:'父组件的值',
            count:0
        },
        components:{
            childComponent
        }
    })
</script>

1.2 通过$parent获取父组件实例的方法或者属性

这种方式,从严格意义上讲不是值的传递,而是一种 “取” (不推荐直接通过实例进行值的获取)

实例属性$parent可以获得父组件的实例,借助实例可以调用父实例中的方法,或者父实例上的属性

<div id="app">
    <child-component :msg="message" :count="count"></child-component>
    <button @click="count++">
        点击+1
    </button>
</div>
<script>
    //子组件
    const childComponent = {
        data:() => ({
            msg:'',
            count:null
        }),
        methods: {
            handleClick(){
                //调用父级实例的方法
                this.$parent.parentMethods();
            }
        }
        mounted () {
            // 获取父级实例中的属性
            this.msg = this.$parent.message;
            this.count = this.$parent.count;
        }
        template: `<div><p @click="handleClick">{{msg}}</p><p>{{count}}</p><div>`
    }
    // 父级
    new Vue({
        el: '#app',
        data: {
            message: '父组件的值',
            count: 0
        },
        methods: {
            parentMethod () {
                console.log('我是父级的方法');
            }
        }
        components: {
            childComponent
        }
    })
</script>

1.3 使用修饰符.sync

修饰符.sync2.3.0+新增,它对props起到了一种修饰的作用,使用.sync进行修饰的props,意味着子组件有修改它的意图,这种情况下它只起到一个标注性作用,有它没它都不会影响逻辑

使用.sync修改上边的代码:

// 父组件 List.vue
<template>
    <!-- 这里不写 .sync 也不会影响结果 -->
    <List-item :title.sync="title" @update:title="updataTitle"></List-item>
</template>
<script>
    import ListItem from "./ListItem";
    export default {
        data() {
            return {
                title: "我是title",
            }
        },
        components: {
            ListItem
        },
        methods: {
            updataTitle(res) {
                this.title = res;
            }
        }
    }
</script>

// 子组件 ListItem.vue
<template>
    <div>
        <button @click="handleClick">Click me</button>
        <div>{{title}}</div>
    </div>
</template>
<script>
    export default {
        props: {
            title: String, 
        },
        methods: {
            handleClick() {
                // 子组件向父组件传值
                this.$emit('update:title', '我要父组件更新 title');
            }
        }
    }
</script>

使用.sync向子组件传递多个props

当我们用一个对象同时设置多个prop的时候,也可以将这个.sync修饰符和v-bind配合使用:

<text-document v-bind.sync="doc"></text-document>

这样会把doc对象中的每一个属性(如:title)都作为一个独立的prop传进去,然后各自添加用于更新的v-on监听器。

二、子组件向父组件传值

2.1 通过事件传值$emit

使用:

子组件使用$emit发送一个自定义事件

父组件使用指令v-on监听子组件发送的事件

<div id="app">
    <child-component @child-event="childEvent"></child-component>
</div>
<script>
    //子组件
    const childComponent={
        data:()=>({
            msg:'点击发送值到父组件',
            count:null
        }),
        methods:{
            handleClick(data){
                this.$emit('child-event','我是子组件传过来的值');
            }
        }
        template:`<div><p @click="handleClick">{{msg}}</p><p>{{count}}</p><div>`
    }
    // 父级
    new Vue({
        el:'#app',
        data:{
            message:'父组件的值',
            count:0
        },
        methods:{
            childEvent(data){
                console.log("子组件传过来的值",data);
            }
        }
        components:{
            childComponent
        }
	})
</script>

2.2 通过$children获取子组件实例

$parent

2.3 通过ref注册子组件引用

虽然存在prop和事件,但是有时仍可能需要在 JavaScript 里直接访问一个子组件。为了实现这样的需求,可以使用ref特性为某个子组件设置一个 ID 引用,就是一个身份标识

<div id="app">
    <child-component ref="childComponent"></child-component>
    <button @click="getRefs">
        获取子组件实例
    </button>
</div>
<script>
    //子组件
    const childComponent={
        data:()=>({
            msg:'点击发送值到父组件',
            count:null
        }),
        template:`<div><p @click="handleClick">{{msg}}</p><p>{{count}}</p><div>`
    }
    // 父级
    new Vue({
        el:'#app',
        data:{
            message:'父组件的值',
            count:0
        },
        methods:{
            getRefs(){
                console.log("子组件传过来的值",this.$refs.childComponent.msg);
            }
        }
        components:{
            childComponent
        }
	})
</script>

三、兄弟组件传值

3.1Bus中央事件总线

非父子组件传值,可以使用一个空的Vue实例作为中央事件总线,结合实例方法$on$emit使用

注意:

注册的Bus要在组件销毁时卸载,否则会多次挂载,造成触发一次但多个响应的情况。

beforeDestroy(){
    this.$Bus.$off('方法名',value);
}

Bus定义方式:

  • 1,将Bus抽离出来,组件有需要时引入

    // bus.js
    import Vue from 'vue';
    const Bus = new Vue();
    export default Bus;
    
  • 2,将Bus挂载到 Vue 根实例的原型上

    import Vue from 'vue';
    Vue.prototype.$bus = new Vue();
    
  • 3,将Bus注入到 Vue 根对象上

    import Vue form 'vue';
    const Bus = new Vue();
    new Vue({
        el:'#app',
        data:{
            Bus
        }
    })
    

    使用例子:

    <div id="app">
        <child-component ></child-component>
        <child-component-two ></child-component-two>
    </div>
    <script>
        Vue.prototype.$bus = new Vue();
        //子组件1 
        const childComponent={
            data:()=>({
                msg:'我是子组件一',
                sendMsg:'我是子组件一发送的值'
            }),
            methods:{
                handleClick(){
                    this.$Bus.$emit('sendMsg',this.sendMsg);
                }
            }
            template:`<div><p @click="handleClick">{{msg}}</p></p><div>`
        }
        //子组件2
        const childComponentTwo={
            data:()=>({
                msg:'我是子组件二',
                brotherMsg:''
            }),
            mounted(){
                this.$Bus.$on('sendMsg',data=>{
                    this.brotherMsg = data;
                })
            },
            beforeDestroy(){
                this.$Bus.$off('sendMsg');
            }
            template:`<div><p @click="handleClick">{{msg}}</p><p>{{brotherMsg}}</p><div>`
        }
    
        // 父级
        new Vue({
            el:'#app',
            data:{},
            components:{
                childComponent,
                childComponentTwo
            }
        })
    </script>
    

四、跨组件

4.1$attrs$listeners

如果父组件 A 下面有子组件 B,组件 B 下面有组件 C,这时如果组件 A 直接想传递数据给 组件 C,那就行不通了!所以,这时可以使用$attrs$listeners

Vue 2.4 提供了$attrs$listeners来实现能够直接让 组件 A 传递消息给 组件 C

// 组件A
Vue.component('A', {
    template: `
        <div>
            <p>this is parent component!</p>
            <B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
        </div>
    `,
    data() {
        return {
            message: 'hello',
            messagec: 'hello c' //传递给c组件的数据
		}		
	},
    methods: {
        // 执行B子组件触发的事件
        getChildData(val) {
       		console.log(`这是来自B组件的数据:${val}`);
        },

        // 执行C子组件触发的事件
        getCData(val) {
        	console.log(`这是来自C组件的数据:${val}`);
        }
	}
});

// 组件B
Vue.component('B', {
    template: `
        <div>
            <input type="text" v-model="mymessage" @input="passData(mymessage)"> 
            <!-- C组件中能直接触发 getCData 的原因在于:B组件调用 C组件时,使用 v-on 绑定了 $listeners 属性 -->
            <!-- 通过v-bind 绑定 $attrs 属性,C组件可以直接获取到 A组件中传递下来的 props(除了 B组件中 props声明的) -->
            <C v-bind="$attrs" v-on="$listeners"></C>
        </div>
    `,
    /**
    * 得到父组件传递过来的数据
    * 这里的定义最好是写成数据校验的形式,免得得到的数据是我们意料之外的
    *
    * props: {
    *   message: {
    *     type: String,
    *     default: ''
    *   }
    * }
    *
    */
	props: ['message'],
	data () {
        return {
            mymessage: this.message
        }
	},
    methods: {
        passData(val){
            //触发父组件中的事件
            this.$emit('getChildData', val)
        }
    }
});

// 组件C
Vue.component('C', {
    template: `
        <div>
            <input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)">
        </div>
    `,
    methods: {
        passCData(val) {
            // 触发父组件A中的事件
            this.$emit('getCData',val)
        }
    }
});

var app = new Vue({
    el:'#app',
    template: `
        <div>
            <A />
        </div>
    `
});

4.2provideinject

熟悉 React 开发的同学对 Context API 肯定不会陌生吧!在 Vue 中也提供了类似的 API 用于组件之间的通信。在父组件中通过provide来提供属性,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject,那么就可以注入在provide中提供的数据,而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。这和 React 中的 Context API 有没有很相似!

// 定义 parent 组件
Vue.component('parent', {
    template: `
        <div>
            <p>this is parent component!</p>
            <child></child>
        </div>
    `,
    provide: {
        for:'test'
    },
    data() {
        return {
            message: 'hello'
        }
    }
});

// 定义 child 组件
Vue.component('child', {
    template: `
        <div>
        	<input type="tet" v-model="mymessage"> 
        </div>
    `,
    inject: ['for'],    // 得到父组件传递过来的数据
    data(){
        return {
            mymessage: this.for
        }
    },
});

const app = new Vue({
    el: '#app',
    template: `
        <div>
        	<parent />
        </div>
    `
});

上面的实例中,定义了组件parent和组件child,组件parent和 组件child是父子关系。

  • parent组件中,通过provide属性,以对象的形式向子孙组件暴露了一些属性
  • child组件中,通过inject属性注入了parent组件提供的数据,实际这些通过inject注入的属性是挂载到 Vue 实例上的,所以在组件内部可以通过this来访问

注意:

官网文档提及 provide 和 inject 主要为高阶插件 / 组件库提供用例,并不推荐直接用于应用程序代码中。

4.3 Vuex 状态管理

Vuex 是状态管理工具,实现了项目状态的集中式管理。工具的实现借鉴了 Flux、Redux 和 The Elm Architecture 的模式和概念。当然与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。

posted on 2021-02-18 22:37  1763392456  阅读(89)  评论(0编辑  收藏  举报

导航