# 组件间通信

# props

props 组件是组件通信最常用最简单的方式。它适用于父子组件之间进行通信。父组件可以给子组件传递函数数据和非函数数据,当我们给子组件传递一个非函数数据通常给子组件传递的是一个父组件配置对象里的一个属性,而不是具体的属性值。子组件拿到了这个属性就是拿到了它的引用,从而可以开始操作属性值。当我们传递非函数数据时,本质上就是父组件给子组件传递数据,以供子组件使用。

当我们给子组件传递的是一个函数数据而时,本质上就是父组件想要得到子组件中收集来的数据,子组件通过给父组件传递过来的函数传递实参的方式,将收集到的数据给父组件。

使用 props 方法通信主要是运用在父子(嵌套)关系里,若想给兄弟组件传递数据就必须经过组件的共同祖先来中转再传递给兄弟组件,十分麻烦。它能够让父组件数据传递给子组件的原因是因为,站在父组件这个层级,父组件能够看到子组件,而子组件看上层组件是看不见的。从我们拆分的语法也可以很容易理解,当我们引入一个文件时,使用 import 引入文件的那个文件能够清楚地知道自己引用了哪些文件,而对于被引用的文件它其实并不知道自己被谁引用了。

// 子组件配置对象
{
    // 第一种写法
    props:['传递过来的属性名']
    // 第二种写法
    props:{
        传递过来的属性名:Function  // 可以指定传递过来的数据类型,这里指定为 Function
    }
    // 第三种写法
    props:{
        传递过来的属性名:{
            type:Function // 指定数据类型
            required:true // 指定必须传递
            defaulte:value // 指定如果没传数据,默认数据的 value
        }
        
    }
}

# 自定义事件

自定义事件通信类似于 props 中父组件给子组件传递函数数据。自己定义事件和自定义回调函数,和系统事件触发相比。我们自己可以定义无数个事件类型,而系统事件类型是固定的;在系统事件的回调函数中,回调函数是由系统调用的,在自定义事件中回调函数是我们自己使用 $emit 函数触发的。

自定义事件专门用于子组件向父组件通信,但不能用于父组件向子组件通信。在组件的层级结构中,父组件是最顶层的可以看见下层的子组件,所以可以用 $on 来给子组件绑定事件,而子组件位于下层无法使用 $on 来给父组件绑定事件。这就导致了自定义事件这种通信类型无法用于父组件向子组件通信传递数据。总结起来就是:

  • 接受数据的组件必须能看到预绑定事件的组件对象,才能绑定
  • 发送数据的组件必须能看到绑定了事件的组件对象,才能触发事件
// 父组件 App.vue
<Add ref="add" ></Add>
{
    // 挂载后才给子组件绑定事件
    monted(){
        this.$refs.add.$on('addUser',function(){  /*....*/ }); // 给子组件绑定 addUser 事件
    }
}
------
// 绑定事件简便写法
<Add @addUser="addU"></Add>
// 子组件 Add.vue
<button @click="addSome"></button>
addSome(){
    let obj = {
        name:'Asuhe',
        age:18
    }
    this.$emit('addUser',obj); // 触发 addUser 事件
}
// 解绑事件
$off('EventName');
// 绑定只能触发一次的事件
$once('EventName',callback);

子组件和父组件能够使用 $on$emit 等函数是因为,子组件的实例化对象的 __proto__ 会顺着原型链往上找到 Vue 方法的 prototype ,在 Vue 这个函数对象的 prototype 中有 $on 这些方法。

原型链查找$on

# 全局事件总线通信

全局总线通信就是自定义事件通信的升级版,它也是利用原型链查找的特性来构建通信,它可以适用任何通信场合。使用全局事件总线通信首先我们要定义一个总线,这个总线是全部组件都可以通过原型链去查找到它,其次这个总线也要有能够使用 $on$emit 函数的权利。只有符合上述两点的对象才能当作总线。根据上述自定义事件原型链,我们可以找到对象 vm 来充当总线。

// 在 main.js 中给 vm 的 prototype 添加一个 $bus 属性让其指向 vm 自身
beforeCreate(){
    vm.prototype.$bus = this; // 数据代理前就挂载总线
}
//App 组件中让 $bus 绑定事件
mounted(){
    this.$bus.$on('Event_name',callback);
}
// 子组件中触发 $bus 上的事件
this.$bus.$emit('Event_name',data);

通过原型链的查找, $busvmvm.__proto__Vue.prototype 的环形查找,就可以使全局事件总线达到所有的组件对象都能找到它和可以调用 $on$emit 的条件。

全局事件总线

完成全局事件总线的设置后,我们只需要将自定义事件绑定在全局事件总线中。在接收数据的组件中,获取到总线然后绑定事件,传入自己定义的回调函数。在发送数据的组件中,获取到总线然后触发事件,传入需要发送的数据。

兄弟组件通信

# 消息订阅和发布

这种通信方式是通过使用第三方插件 PubSubJS 的方式来达到通信目的。其使用方法类似于全局事件总线。适用于任何组件之间进行通信,但是互相通信的组件之间都需要分别引入该插件

接收数据的组件订阅事件源 (类似 $emit ),发送数据的组件绑定事件源 (类似 $on )。

# xxxxxxxxxx11 1Vue.filter('timeFormat',function(value,format='YYYY-MM-DD hh:mm:ss'){2    return moment(value).format(format)3})4const vm = new Vue({5    el:'#app',6    data(){7        return {8            timeNow: Date.now()9       }10   }11})js

当一个组件会被多次使用,且里面大部分内容不变仅有非常少部分的结构改变时,可以使用 slot 插槽,将其理解为占位符。该种通信方式适用于父组件给子组件传递数据,但它与其它通信不同的是,它可以传递结构给子组件,子组件中的 slot 标签本质上就是一个占位符。若父组件给其传递 template 则使用父组件传递过来的 template ,否则使用默认定义的 template 。插槽又分为默认插槽、具名插槽和作用域插槽。

默认插槽约定成俗只能有一个,具名插槽就是在默认插槽的基础上加上 name 属性唯一标识这个插槽,这样父组件在传递数据的时候可以根据名字精准传递到指定的插槽中。

作用域插槽中子组件的 slot 可以通过 属性传递值给父组件,然后父组件可以根据不同需求改变这个 slot 内部的显示结构,把子组件的值,传给父组件固定的区域进行操作。父组件的数据是给子组件展示的。子组件展示过程当中,数据的结构由父组件决定的。

默认插槽和具名插槽

// 子组件放置插槽
<slot></slot>
<slot name="asu"></slot>
// 父组件传递数据
<template>
    <button>点击<button>
</template>
<template slot="asu">
    <a href="https://asuhe.fun"></a>
</template>

作用域插槽

// 父组件
<template slot-scope="{todo,index}">
	<span v-if="todo.isOver" style="color:hotpink"><!--swig0--></span>  // 父组件控制子组件的样式
</template>
// 子组件
<slot :todo="todo" :index="index">
    <!--swig1-->
</slot>

# Vuex 通信

vuex 是一个 vue 官方推出的状态管理插件。它采用集中式存储管理应用中所有组件的状态,并以相应的规则保证状态可以以一种可预测的方式发生变化。我们可以将它认为是一种组件间通信的方式,** 它适用于任何组件间通信。** 它能够对应用中多个给组件共享的状态(数据)进行集中式的管理(读写)。

在以往当有多个视图(组件)依赖统一个状态时,或者说来自不同视图的行为需要变更同一状态,从前我们都是将数据以及操作定义在父组件,将数据以及操作数据的行为传递给各个需要使用这些数据和行为的子组件(有时可能需要多级传递)。这种方式去管理状态,当应用比较复杂时传递逻辑就会逐渐变得复杂,且代码后期维护也会变得更加困难,为了解决这个问题所以就出现了 Vuex。Vuex 就类似于一个数据管理的仓库,它存储着数据所有关于数据的操作都定义在它内部,当需要使用数据时仅需调用它提供的接口即可,这就是集中式管理状态。

# Vuex 的五个核心概念

  • state:代表数据的初始状态,本质上就是包含多个属性的对象,属性的值只能是数据,不能是函数这样的东西
  • getters:代表计算属性数据,是一个包含 n 个计算属性方法的对象,处理同步任务
  • actions:代表用户行为,是一个包含多个方法的对象,方法里面可以进行 for、if 等等判断操作。作为对用户提供操作数据的接口,处理异步任务
  • mutations:代表数据操作,是一个包含多个方法的对象,方法里面只能进行纯粹的数据操作不能进行 if 等判断操作,作为 actions 操作数据的接口
  • Modules:代表事件处理,用于多模块时使用

# 基本使用步骤

  • 引入 vuex 并声明
  • 向外暴露一个 Store 对象
  • 将暴露出去的 Store 对象引入到实例化 Vue 的配置对象当中使用,Vue.use 全局注册
  • 在 store 对象中定义好五个核心概念

Vuex作用原理

所有从 state 里取出的数据都应该放在 computed 中,根据 state 中的数据变化而变化。若放在 data 中则会导致只能拿到初始的数据。

更新于 阅读次数