Vue组件通信方式你不一定都知道

178 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

一、前言

在vue项目中关于Vue组件间的通信方式有很多种,也是面试高频问点,今天就对这些通信方式做一个简单的总结和归纳。

二、归纳一下什么情况用哪种

父子组件

  1. props
  2. vue自定义事件
  3. v-model
  4. sync
  5. $ref, $children$parent
  6. 插槽 ==> 作用域插槽

祖孙组件

  1. $attrs$listeners
  2. provide与inject

兄弟或其它/任意

  1. 全局事件总线
  2. Vuex

三、组件间通信方式具体

1. props/$emit

  • 实现父向子通信: 属性值是非函数
  • 实现子向父通信: 属性值是函数
   //父组件
   <template>
     <son :msg="msg" @search-list="searchList"></son>
   </template>
   export defalut{
     data(){
       name:'Father'
       return{
         msg:'lucky'
         searchForm:{}
       }
     },
     methods:{
       searchList(val){
         //val即可拿到子组件传过来的参数
         this.searchForm = val
       }
     }
   }
  
   
   //子组件
   <template>
      <div @click="search"></div>
   </template>
   export defalut{
     data(){
       name:'Son',
       //子组件用父组件传过来的
       props:{
        msg:{
          type:String
          default:''
        }
       }
     },
      method:{
          saerch(){
             //this.$emit(方法名,参数) 子组件向父组件
             this.$emit('search-list',list)
          }
         }
   }
   
   

2. vue自定义事件

用来实现子组件向父组件通信

  • 实现
    • 父组件中绑定自定义事件监听:<Child @eventName="callback">
    • 子组件中分发事件this.$emit('eventName', data)
  • 应用
    • elment-ui的组件的事件监听语法都用的是自定义事件,项目里自定义事件用的也比较多

3. 全局事件总线   ===> 消息订阅与发布

实现任意组件间通信

  • 实现
    • 将入口js中的vm作为全局事件总线对象: beforeCreate() {Vue.prototype.$bus = this}
    • 分发事件/传递数据的组件: this.$bus.$emit('eventName', data)
    • 处理事件/接收数据的组件: this.$bus.$on('eventName', (data) => {})
  • 应用
    • 前台项目中使用全局事件总线

4. v-model

实现父子之间相互通信/同步

  • 实现
//父组件
 <CustomInput v-model="name"/>
   <!-- 等价于 -->
<CustomInput :value="name" @input="name=$event"/>

//子组件
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
props: ['value']
  • 应用
    • element-ui中的表单项相关组件都用了v-model: Input / Select / Checkbox / Radio
  • 注意
    • 组件标签上的v-model的本质: 动态value属性与自定义input监听来接收子组件分发的数据更新父组件数据

5. .sync

实现父子之间相互通信/同步(在原本父向子的基础上增加子向父)

  • 实现
    • 组件标签的属性上使用.sync的本质: 通过事件监听来接收子组件分发过来的数据并更新父组件的数据
 //父组件
  <child :money.sync="total"/>
  <!-- 等价于 -->
  <Child :money="total" @update:money="total=$event"/>
    
    data () {
      return {
        total: 1000
      }
      
 //子组件
  <button @click="$emit('update:money', money-100)">花钱</button>
  props: ['money']
  • 应用
    • element-ui在有显示隐藏的组件上: Dialog / Drawer

6. $attrs$listeners

  • $attrs
    • 实现当前组件的父组件向当前组件的子组件通信
    • 它是包含所有父组件传入的标签属性(排除props声明, class与style的属性)的对象
    • 使用: 通过 v-bind="$attrs" 将父组件传入的n个属性数据传递给当前组件的子组件
  • $listeners
    • 实现当前组件的子组件向当前组件的父组件通信
    • $listeners是包含所有父组件传入的自定义事件监听名与对应回调函数的对象
    • 使用: 通过v-on="$listeners" 将父组件绑定给当前组件的事件监听绑定给当前组件的子组件
     //父组件
     <template>
            <son1 :title="title" :msg="msg"></son1>
     </template>
     
     //子组件1 son1
      <template>
            <div>
                <div>{{title}}</div>
                <div>{{$attrs.msg}}</div>
                <son2 v-bind="$attrs"></son1>
            </div>
     </template>
     <script>
         export default{
             props:['title']
         }
     </script>
     
     //子组件2 son2
      <template>
            <div>
                <div>{{$attrs.title}}</div>
                <div>{{$attrs.msg}}</div>
            </div>
     </template>

7. $refs & $children & $parent

  • $children
    • 实现父组件向多个子组件通信
    • $children是所有直接子组件对象的数组
    • 使用: 通过this.$children 遍历子组件对象, 从而可以更新多个子组件的数据
  • $parent
    • 实现子组件向父组件通信
    • $parent是当前组件的父组件对象
    • 使用: 通过this.$parent 得到父组件对象, 从而可以更新父组件的数据
   //父组件
   <template>
       <div>
           <div>{{msg}}</div>
              <son></son>
              <botton @click="change">改变子组件</button>
       </div>
   </template>
   export default{
     name:'Father',
     data(){
       return{
         masg:'lucky'
       }
     },
     method:{
        change(){
           //获取第一个子组件的数据
           this.$children[0].msg = 'this is lucky'
        }
     }
   }
   
   //子组件
   <template>
       <div>
           <div>{{msg}}</div>
           <div>获取父组件的值:parentValue()</div>
       </div>
   </template>
   export default{
     name:'Son',
     data(){
       return{
         masg:'lucky'
       }
     },
     computed:{
       parentValue(){
           return this.$parent.msg
        }
     }
   }
  • $refs
    • 实现父组件向指定子组件通信
    • $refs是包含所有有ref属性的标签对象或组件对象的容器对象
    • 使用: 通过 this.$refs.child 得到子组件对象, 从而可以直接更新其数据或调用其方法更新数据
    //子组件
    export deault{
      data(){
        return{
           msg:'lucky'
        }
      }
      methods:{
         do(){
           console.log('what are you doing')
         }
      }
    }
 
    //父组件
    <template>
         <son ref='son'></son>
    </temaplate>
    <script>
          export default{
                mounted(){
                   let msg = this.$refs.msg
                   this.$refs.son.do()
                }
          }
    </script>

8. provide与inject

实现祖孙组件间直接通信

  • 使用
    • 在祖组件中通过provide配置向后代组件提供数据
    • 在后代组件中通过inject配置来声明接收数据
    //顶层父组件
    export default{
       provide(){
         mag:'lucky'
       }
    }
    
    //后代组件 不限于多少层嵌套
    export default{
      inject:{'msg'},
      data(){
        return{
          msg:this.msg
        }
      }
    }
  • 注意
    • 不太建议在应用开发中使用, 一般用来封装vue插件
    • provide提供的数据本身不是响应式的 ==> 父组件更新了数据, 后代组件不会变化
    • provide提供的数据对象内部是响应式的 ==> 父组件更新了数据, 后代组件也会变化
  • 应用
    • element-ui中的Form组件中使用了provide和inject

9. vuex

vuex用来统一管理多个组件共享的状态数据,任意要进行通信的2个组件利用vuex就可以实现

  • 实现
    • A组件触发action或mutation调用, 将数据保存到vuex的状态中
    • B组件读取vuex中的state或getters数据, 得到最新保存的数据进行显示

10. 插槽  ==> 作用域插槽slot-scope

实现父组件向子组件传递标签内容

  • 什么情况下使用作用域插槽?
    • 父组件需要向子组件传递标签结构内容,但决定父组件传递怎样标签结构的数据在子组件中
  • 实现
//子组件:
<slot :row="item" :$index="index">  <!-- slot的属性会自动传递给父组件 -->
</slot>

//父组件:
<template slot-scope="{row, $index}">
    <span>{{$index+1}}</span> &nbsp;&nbsp;
    <span :style="{color: $index%2===1 ? 'blue' : 'green'}" >{{row.text}}</span>
</template>
  • 应用
    • element-ui中的Table组件

好了,以上就是本篇文章的分享,感谢阅读!