Vue从甜小白到皮大佬系列(六) 组件通信

4,477 阅读4分钟

🎉 Vue构建大型单页面电商应用 开源啦!点我看源码🚀🚀

阅读时间预估:8分钟

组件之间通信是Vue中最常用,最基础的部分,通常组件之前的通信分为以下几种:

  • 1.父组件给子组件传递值,传递事件
  • 2.子组件给父组件传递值,传递事件
  • 3.兄弟组件之间传递值,传递事件

方法一、props $emit v-on

1.父组件给子组件传值

父组件A通过props向子组件B通信

接下来我们通过一个例子来加深以上理解:

自定义一个Son.vue子组件,引入到父组件中 在父组件中定义一个数据

<template>
    <div id="app">
        <!-- 前者自定义名称便于子组件调用,后者要传递数据名 -->
        <son v-bind:user="user"></son>  
    </div>
</template>
<script>
import Son from './components/Son'

export default {
    name: 'app',
    components: {
        Son,
    },
    data () {
        return { // 父组件定义数据,传递给子组件
            user: ["james", "alice", "joho"]
        }
    },
  }
</script>
    

子组件中通过prop来定义父组件传值的值类型,是否是必须以及默认值

<script>
export default {
    components: {

    },
    data () {
        return {

        };
    },
    computed: {

    },
    methods: {
    },
    props: {
        user: { //这个就是父组件中子标签自定义名字
            type: Array,
            required: true,
            default: []
        }
    },
}
</script>

然后在适当的位置将父组件传递过来的数据进行渲染

<template>
    <div>
        <ul>
            <li v-for="(item,index) in user"
                :key="index">姓名:{{item}}</li>
        </ul>

    </div>
</template>

浏览器打开会显示user里面的值.

总结:父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed

2.子组件向父组件传值(通过自定义事件形式)

核心点

  • 1.子组件使用this.$emit('fn',param)向父组件传值

  • 2.父组件通过v-on:fn="fn"来接受子组件传递过来的值

子组件中:

子组件包含一个点击事件

// 定义一个点击事件
 <son v-bind:user="user"
             @titleChange="changeTitle">
</son>

在点击事件中通过this.$emit()来传递事件

<script>
//import x from ''
export default {
    components: {

    },
    data () {
        return {
            title: '我是子组件',
            toParentData: '我是子组件传递的数据'
        };
    },
    computed: {

    },
    methods: {
        btnClick () {
            this.$emit('titleChange', this.toParentData);
        }
    },
    }
</script>

父组件中: 通过v-on来接受子组件发出的事件名称:titleChange并且和自己的changeTitle事件绑定

<template>
    <div id="app">
        <!-- 前者自定义名称便于子组件调用,后者要传递数据名 -->
        <son v-bind:user="user"
             v-on:titleChange="changeTitle"></son>
        <p>{{msg}}</p>
    </div>
</template>

实现changeTitle方法,改变data中的msg数据

<script>
import Son from './components/Son'

export default {
    name: 'app',
    components: {
        Son,
    },
    data () {
        return {
            user: ["james", "alice", "joho"],
            msg: '我是父组件显示的内容'
        }
    },
    methods: {
        changeTitle (title) {
            this.msg = title;
        }
    }
}
</script>

总结:子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

方法二 中央事件总线 Bus 进行通信

发现了一个问题,如果孙子组件给爷爷组件传值,通过props或者$emit方式是一件很痛苦的事情,需要通过中间的爸爸来做衔接,这样通信显然会使得组件耦合,同时兄弟组件间的通信props,$emit也实现不了?那么问题来了,如何解决呢?

这里介绍中央事件总线的方式,名字高大上其实就是抽一个公共Vue实例媒介来管理,需要通信的组件都引入Bus,之后通过分别触发和监听 Bus 事件,进而实现组件之间的通信和参数传递.

  • 1.首先建 Vue 实例作为总线:
// Bus.js
import Vue from 'vue'
export default new Vue;
  • 2.需要通信的组件都引入 Bus.js,使用 $emit发送信息:
// ComponentA.vue
<template>
  <div>
    <b>组件A:</b><button @click="handleBus">传递数值给需要的组件</button>
  </div>
</template>

<script>
  import Bus from './bus.js' 
  export default {
    methods: {
      handleBus () {
        Bus.$emit('someBusMessage','来自ComponentA的数据')
      }
    }
  }
</script>
  • 3.需要组件A信息的就使用$on来监听:
// ComponentB.vue
<template>
  <div>
    <b>组件B:</b><button @click="handleBus">接收组件A的信息</button>
    <p>{{message}}</p>
  </div>
</template>

<script>
  import Bus from './bus.js' 
  export default {
    data() {
      return {
        message: ''
      }
    },
    created () {
      let that = this // 保存当前对象的作用域this
      Bus.$on('someBusMessage',function (data) {
        that.message = data
      })
    },
    beforeDestroy () {
      // 手动销毁 $on 事件,防止多次触发
      Bus.$off('someBusMessage', this.someBusMessage)
    }
  }
</script>

中央总线优点:可以解耦组件,方便兄弟间通信

方法三 $parent / $children & $refs

  • $parent / $children:指定已创建的实例,在两者之间建立父子关系。子实例可以用 this.$parent 访问父实例,子实例被推入父实例的 $children 中。

  • $refs:一个对象,持有注册过 ref特性的所有 DOM 元素和组件实例。ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。

  • 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件。

父组件

<template>
    <div id="app">
        <!-- 前者自定义名称便于子组件调用,后者要传递数据名 -->
        <p>{{msg}}</p>
        <child-one ref="childOne"></child-one>
        <child-two ref="childTwo"></child-two>
        <button @click="oneContent">显示child-one传过来的数据</button>
        <button @click="twoOneContent">显示hild-two传过来的数据</button>

    </div>
</template>

<script>
// 引入子组件
import ChildOne from './components/ChildOne'
import ChildTwo from './components/ChildTwo'
export default {
    name: 'app',
    components: {
        ChildOne,
        ChildTwo
    },
    data () {
        return {
            msg: '我是父组件',
            content: 'James' //子组件要用的数据
        }
    },
    methods: {
        // button 的事件响应 
        oneContent () {
            const childOne = this.$refs.childOne;
            this.msg = childOne.msg;
        },
        twoOneContent () {
            // 通过$refs取到childTwo
            const childTwo = this.$refs.childTwo;
            this.msg = childTwo.msg;
        }
    },
}
</script>

子组件1

<template>
    <div>
        <p>{{title}} 我的父组件是{{content}}</p>
    </div>
</template>

<script>
//import x from ''
export default {
    components: {
    },
    data () {
        return {
            content: '',
            title: '我是子组件 childOne',
            msg: '我只子组件childOne hello ereryOne'
        };
    },
    mounted () {
        // 从父组件里取contet的数据
        this.content = this.$parent.content;
    },
}
</script>

子组件2:

<template>
    <div>
        <p>{{title}} 我的父组件是{{content}}</p>
    </div>
</template>

<script>
//import x from ''
export default {
    data () {
        return {
            content: '',
            title: '我是子组件 childTwo',
            msg: '我是子组件childTwo hello ereryOne'
        };
    },
    mounted () {
        // 加载父组件content的内容
        this.content = this.$parent.content;
    },
}
</script>

方法四: 发布订阅模式

官方GitHub

  • 1.通过npm install pubsub-js --save的方式引入pubsub库
  • 2.创建event-type.js定义Symbol类型的数据并导出
// event-types.js
export const MY_TOPIC = Symbol('MY_TOPIC')
  • 3.在需要发布(传递事件)的地方引入pubsubevent=type.js,并且通过以下方式发布事件
import PubSub from 'pubsub-js'
import { MY_TOPIC } from './event-types.js'
PubSub.publish(MY_TOPIC, 'world');

  • 4.在需要订阅(接受事件)的地方引入pubsubevent=type.js,并且通过以下方式接受事件
import PubSub from 'pubsub-js'
import { MY_TOPIC } from './event-types.js'
PubSub.subscribe(MY_TOPIC, function (msg, data) {
	console.log(data)
});
  • 5.记得在组件销毁之前移除通知哦~
 beforeDestroy() {
    // 销毁通知
    PubSub.subscribe(MY_TOPIC)
    console.log("通知被销毁了!")
  },

方法五 Vuex

官方传送门

Vuex 是Vue官方推荐的一种解决组件中通信的完美解决方案,也是在项目开发中必用的方案:

点我学习Vuex

认真看完本篇后对照思维导图可以在脑海里回顾一遍哦,对此块的知识点你会有更深的认识和理解.

Vue组件思维导图

如果我的分享对面前的这位大侠有所启发,请不要吝啬手中大拇指,以程序员最高礼遇点赞✨ 评论加分享的方式鼓励我持续分享,也欢迎各位大佬勘误,提出宝贵意见.

关注公众号回复:学习 领取前端最新最全学习资料,也可以进群和大佬一起学习交流

猛戳 我的前端进阶Blog