Vue组件通信

91 阅读3分钟

前言

大家对vue组件通信的方式应该都有了解,下面我来说下自己知道的通信方式

  • props

  • $emit 触发自定义事件

  • v-model 双向绑定

  • $refs

  • children/children/parent

  • $root

  • attrs/attrs/listeners

  • slot插槽

  • provide/inject

  • EventBus

  • Vuex

  • 另外还有通过路由的 `params/query/meta` 等传值

  • 浏览器存储

props

最常见的组件通信方式了,常用于父子间通信.

注意: 因为vue中数据是单向向下流动的,所以不要在子组件中直接修改props传递下去的数据

//父组件<template><div class="father"><button @click="changeMsg">修改信息</button><SonComponent :msg="msg"/></div></template>// scriptdata(){  return{   msg:''  }},methods:{  changeMsg(){   this.msg='我是父组件props传过来的数据';  }}


//子组件接收的话有两种方式, 可以写数组或者对象<template><div>我是props数据: {{ msg }}</div></template>// scriptprops:['msg'],// props:{// msg:{//   type: String, // props传递的数据类型//   default: '默认信息', // 默认值//   require: true, // 必传//   validate: /\*/ // 校验规则//  }// },

效果

$emit

一般用于父组件传递事件给子组件,子组件通过$emit进行触发

//父组件
<template>
  <div class="father">
    <SonComponent :msg="msg" @changeMsg="changeMsg"/>
  </div>
</template>

// script
methods:{
  changeMsg(msg){
    msg=msg??'我是父组件props传过来的数据';
    this.msg=msg;
  }
}

//子组件  <div>
    <button @click="$emit('changeMsg','emit修改的数据')">emit修改</button>
    我是props数据: {{ msg }}
  </div>

 效果

v-model

首先我们需要知道,v-model的话是v-on以及v-bind的语法糖,一般我们用于双向绑定在input上,但是我们也可以用其对父子组件进行通信。

vue2 vue3有点区别,可以去看看官方文档 [V2官方文档](v2.cn.vuejs.org/v2/guide/co…)  [V3官方文档](cn.vuejs.org/guide/compo…)

我用的是V2的,需要在写model配置项

//父组件
  <div class="father">
    <SonComponent v-model="msg"/>
  </div>

//子组件
  <div>
    <button @click="$emit('modelChange','model修改的数据')">model修改</button>
    我是props数据: {{ newMsg }}
  </div>

  // script
  model:{
    prop:'newMsg',  //父组件传来的值选择用 名为newMsg 的prop来接收,所以必须下面的props必须要写,且对应
    event:'modelChange' //自定义父组件传递过来的事件名,通过emit触发时,第二个参数为父组件model绑定数据的新值
  },
  props:['newMsg']

 效果

$refs

我们通常会将 $refs绑定在DOM元素上,来获取DOM元素的 attributes。在实现组件通信上,我们也可以将 $refs 绑定在子组件上,从而获取子组件实例。

ref被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 如果用在子组件上,引用就指向组件实例。

 //父组件
<div class="father">
    <button @click="changeMsg">修改信息</button>
    <SonComponent ref="son"/>
    {{ msg }} 
</div>

  // script
  methods:{
    changeMsg(){
      console.log(this.$refs.son)
      this.msg=this.$refs.son.sonMsg;
    }
  }

//子组件
  data() {
    return {
      sonMsg:'我是son中的数据'
    };
  },

效果

通过$refs访问组件得到的是组件实例

children/children/parent

我们可以在 Vue 中直接通过this.$parent来获取当前组件的父组件实例(如果有的话),通过this.$children[index]获取对应的子组件实例

  //父组件
   <div class="father">
    <button @click="changeMsg">修改信息</button>
    <SonComponent/>
    {{ msg }} 
  </div>

  // script
  
  methods:{
    changeMsg(){
      console.log(this.$children) // 获取的是子组件数组,需要通过索引值访问具体的子组件
      this.$children[0].changeSonMsg('Father给你的数据') 
    }
  }

  //子组件
  <div>
    son的sonMsg:{{sonMsg}}
    <br/>
    parent的msg:{{parMsg}}
  </div>

  // script
  data() {
    return {
      sonMsg:'我是son中的数据',
      parMsg:this.$parent.msg
    };
  },
  methods: {
    changeSonMsg(msg){
      this.sonMsg=msg;
    }
  }

 效果

$children获取的子组件是数组,需要用索引获取具体的实例

$parent获取父组件只有一个

$root

获取当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。通过 $root ,我们可以实现组件之间的跨级通信。

通过this.$root获取根实例,注意不是App.vue

attrs/attrs/listeners

attrs:包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (\`class\` 和 \`style\` 除外)。当一个组件没有声明任何 props 时,这里会包含所有父作用域的绑定 (\`class\` 和 \`style\` 除外),并且可以通过 \`v-bind="attrs"` 传入内部组件

listeners:包括父作用域中的 \`v-on\` 事件监听器(但不包括添加了 \`.native\` 修饰器的那些事件监听器)。可以通过 \`v-on="listeners"`,将这些事件监听器向下传入到组件内部

//父组件
  <SonComponent @changeInfo="changeInfo" :info="info" :msg="msg"/>

  // script
  data(){
    return{
      msg:'默认值',
      info:'哈哈哈哈'
    }
  },
  methods:{
    changeInfo(info){
      this.info=info
    },
  }

//子组件
  <p>子组件的$attrs:  {{ $attrs.info }}</p> //子组件可以通过$attr直接获取到父组件传递下来的数据
  <GrandSon v-bind="$attrs" v-on="$listeners"/> //给孙子组件传递$attrs,不能用props接收需要传递下去的数据,不需要传递的可以接收

  // script
  props:['msg'] //接收msg, 那么传递给下级组件的$attrs中就没有msg了

//孙子组件
//触发$listeners传递下来的事件就和触发自定义事件一样 用$emit//孙子的$attrs没有值是因为info被自己接收了、msg被上一级组件接收了  <div>
    <button @click="$emit('changeInfo','孙子组件的传值')">修改爷爷的info</button>
    <p>孙子的props:{{info}}</p>
    <p>孙子的$attrs:{{$attrs}}</p>
  </div>

  // script
  props:{
    info:{
      type:String,
      default:'孙子的默认值'
    }
  },

 效果

slot插槽

  • 匿名插槽(父->子)

子组件定义了solt,但未提供名字,这就是匿名插槽,也叫做默认插槽,只要出现的父组件中,未指定插槽名字的内容,都会默认放到匿名插槽里。

  • 具名插槽(父->子)

所谓具名插槽,就是给插槽命了名字,父组件放进来的内容,需要指定插槽的名称,这个时候才会被分发到这个具名插槽中。

  • 作用域插槽(子->父)

有时让插槽内容能够访问子组件中才有的数据是很有用的。所以提供了作用域插槽

 //父组件
  <SonComponent>
    {/* defaultObj 上获得的值是匿名插槽中 v-bind 绑定的所有值的对象 */}
    <template v-slot:default="defaultObj">  
      <div>
        {{ defaultObj }}
        <p>{{defaultObj.defaultMsg}}</p>
      </div>
    </template>
    {/* 直接将nameMsg解构出来 */}
    <template v-slot:name="{nameMsg}"> 
      <div>
        {{ nameMsg }}
        <p>{{`父组件传递的信息${info}`}}</p>
      </div>
    </template>
  </SonComponent>

  // script
  data(){
    return{
      info:'哈哈哈哈'
    }
  },



  //子组件
  <slot :defaultMsg="defaultMsg"></slot>
  <slot name="name" :nameMsg="nameMsg"></slot>

  // script
  
  data() {
    return {
      defaultMsg:'匿名插槽中使用作用域插槽',
      nameMsg:'具名插槽中使用作用域插槽'
    };
  },

效果

provide/inject

provide:是一个对象,或者是一个返回对象的函数。该对象包含可注入其子孙的 property ,即要传递给子孙的属性和属性值。

Injcet:一个字符串数组,或者是一个对象。当其为字符串数组时,使用方式和props十分相似,只不过接收的属性由data变成了provide中的属性。当其为对象时,也和props类似,可以通过配置default和from等属性来设置默认值,在子组件中使用新的命名属性等。

注意:provideinject绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象(引用类型),那么其对象的属性还是可响应的

    // 爷爷组件
    <button @click="changeMsg('修改后的msg')">修改msg</button>
    <p>爷爷的info:{{info}}</p>
    <p>爷爷的msg:{{msg}}</p>
    <SonComponent/>
    <button @click="changeInfo('修改后的info')">修改info</button> 

    provide(){
      return{
        msg:this.msg,
        info:this.info
      }
    },  
    data(){
      return{
        msg:'默认值',
        info:{msg:'哈哈哈哈'}
      }
    },
    methods:{
      changeInfo(info){
        this.info.msg=info
      },
      changeMsg(msg){
        this.msg=msg;
      }
    }


  // 孙子组件
  <p>孙子收到的msg:{{ msg }}</p>
  <p>孙子收到的info:{{ info.msg }}</p>

    
  inject:['info','msg']

 效果

EventBus 事件总线

eventBus又称事件总线,通过注册一个新的Vue实例,通过调用这个实例的emitemit和on等来监听和触发这个实例的事件,通过传入参数从而实现组件的全局通信。它是一个不具备 DOM 的组件,有的仅仅只是它实例方法而已,因此非常的轻便。

我们可以通过在全局Vue实例上注册:

// main.js 文件中Vue.prototype.$Bus = new Vue()



  // 爷爷组件
  <p>爷爷的msg:{{msg}}</p>
  <SonComponent/>

  
  data(){
    return{
      msg:'默认值'
    }
  },
  created(){
    this.$Bus.$on('changeMsg',this.changeMsg)
  },
  beforeDestroy(){
    this.$Bus.$off('changeMsg')  //组件销毁前 解绑事件
  },
  methods:{
    changeMsg(msg){
      this.msg=msg;
    }
  }
  
  
  // 孙子组件
  <button @click="$Bus.$emit('changeMsg','孙子组件触发了changeMsg事件')">触发爷爷组件上的事件</button>

vuex 大家可以直接去看看,挺多的