Vue-组件通信6种方式

  • Post author:
  • Post category:vue

不熟悉组件通信,写分页器组件时太难受了…

b站视频链接

组件通信6种方式

今天面试被问到了兄弟组件通信,突然有点懵,只想出了一个全局事件总线
Vue 兄弟组件之间的通信

1.props

适用场景:父子组件通信

2.自定义事件

适用场景:子组件给父组件通信
$on$emit

3.全局事件总线 $bus

适用场景:万能
Vue.prototype.$bus=this;
$emit发布事件,$on监听事件
详情参考这篇

4.pubsub-js

适用场景:万能
React框架中使用比较多,套路:发布与订阅

5.Vuex

适用场景:万能

6.插槽

适用场景:父子组件通信
三种:默认插槽、具名插槽、作用域插槽

自定义事件深入

event:事件对象

  • 事件分为系统事件(click、双击、鼠标系列…)和自定义事件
  • 无论是系统事件还是自定义事件,都要注意三点:事件源(事件是给谁绑定的)、事件类型(单机还是双击等等)、事件回调
  • 原生DOM绑定系统事件(原生事件):比如button可以绑定click单击事件等
  • 对于组件标签,也可以绑定系统事件,但会不起作用:比如给Event组件绑定click事件,在此click事件就不是原生事件,而属于自定义事件。若想给组件标签绑定原生事件,加个时间修饰符.native即可。也就是 <Event @click.native="handler"></Event> 当前原生DOM click事件,其实是给组件的根标签绑定了点击事件,此处利用了事件委派
    注:.natve在vue3中已被废弃
  • 给原生DOM绑定自定义事件没任何意义,因为没办法触发$emit函数

v-model深入

v-model是Vue框架中的指令,主要结合表单元素(比如文本框、复选、单选按钮等)一起使用,主要作用是收集表单元素,它也是组件通信方式的一种,可以实现数据的双向绑定

v-model基本使用:收集表单数据

<template>
  <div>
    <h2>深入v-model</h2>
    <input type="text" v-model="msg">
    <span>{{msg}}</span>
    <br>
  </div>
</template>

<script type="text/ecmascript-6">
 // import CustomInput from './CustomInput.vue'
  export default {
    name: 'ModelTest',
    components: {
      // CustomInput
    },
    data(){
      return {
        msg:'赵丽颖'
      }
    }

  }
</script>

v-model实现原理::value + @input

<input type="text" :value="msg" @input="msg = $event.target.value">

Vue2可通过value与input事件实现v-mdel功能:原生DOM中有oninput事件,它经常结合表单元素一起使用,当表单元素文本内容发生变化时就会触发一次回调。

深入学习v-model,实现父子组件数据同步(父子组件通信)

<input type="text" :value="msg" @input="msg = $event.target.value">

👆此处的:value是给原生DOMinput绑定一个响应式属性

<CustomInput :message="msg" @input="msg = $event"></CustomInput>

👆此处的:valueprops,父子组件通信

子组件如下:

<template>
  <div style="background: #ccc; height: 50px;">
    <h2>input包装组件</h2>
    <input type="text" :value="message" @input="$emit('input',$event.target.value)">
  </div>
</template>

<script type="text/ecmascript-6">
  export default {
    name: 'CustomInput',
    props:['message']
  }
</script>

属性修饰符sync

效果图👇

  • 不使用sync修饰符
<!-- 	  :money 父组件给子组件传递props
          @update:money  给子组件绑定的自定义事件  名字是update:money
          这种操作其实和v-model很相似,可以实现父子组件数据同步
 -->
 
    <Child :money="money" @update:money="money = $event"></Child>
  • 使用sync修饰符
<!--  :money.sync 含义:
      第一,父组件给子组件传递props --money
      第二,给当前子组件绑定了一个自定义事件  而且事件名称即为update:money
    -->
<Child :money.sync="money"></Child>

注:.syncv-model用在组件标签上,都可以达到父子组件数据同步
区别:
.sync达到数据同步: 子组件内部不是表单类元素
v-model达到数据同步:子组件内部一定是表单类元素

$attrs与$listeners

组件实例自身的$attrs$listeners属性:
$attrs 属于组件的一个属性,可以获取到父组件传递过来的props数据。 对于子组件而言,父组件给的数据可以利用props接收,但要注意,子组件通过props接收的属性在$attrs属性当中是获取不到的。

$listeners可以获取到父组件给子组件传递的自定义事件

el-button进行二次封装:自定义带Hover提示的按钮(利用a标签的title属性)

子组件如下:
:type="$attrs.type"这种写法是一个一个绑定,用v-bind="$attrs"可以把$attrs接收到的所有属性绑定到元素上

<template>
  <a href="javascript:;" :title="title" style="margin-right:10px">
  <!-- 此处v-bind不能用 : 代替  v-on不能用@代替 -->
    <el-button v-bind="$attrs" v-on="$listeners"></el-button>
  </a>
</template>

<script>
export default {
  name: '',
  props:['title'],
  mounted(){
    //this.$attrs 就是父组件传递过来的所有属性(除了props接收的以及style和class)组成的对象
    //this.$listeners 就是父组件给子组件绑定的所有事件监听组成的对象
    //通常使用他们就是为了对一个组件进行二次封装
    console.log(this.$attrs,this.$listeners)
  }
}
</script>

$children与$parent

效果图👇

ref

ref实现BABA找儿子借钱,ref可以获取真实DOM节点,当然也可以获取子组件标签(操作子组件的数据与方法):

<button @click="borrowMoneyFromXM(100)">找小明借钱100</button><br>

<Son ref="son" />
    borrowMoneyFromXM(sonmoney){
      //父亲的钱要加 + 100
      this.money += sonmoney
      //儿子小明的钱要 - 100
      this.$refs.son.money -= sonmoney
    },

用同样的方法可以实现向女儿借钱

$children

当同时向儿子和女儿借钱时:

<button @click="borrowMoneyFromALL(200)">找所有孩子借钱200</button><br>
borrowMoneyFromALL(money){
      this.money += money * 2
      //儿子和女儿都要减去200
      // 组件实例自身拥有一个属性$children 可以获取当前组件当中的所有子组件
      console.log(this.$children)
      this.$children.forEach(item => item.money -= money)
      // this.$children[0]  不一定是谁,因此官方建议尽量不使用$children和$parent
    }

$parent

当儿子女儿给BABA钱时,也就是子组件操作父组件的数据时:
需要在子组件内部,获取到父组件,让父组件的数据发生变化。可以通过$parent属性获取到某一个组件的父组件,然后操作父组件的数据和方法

<button @click="giveMoney(50)">BABA: 50</button>
giveMoney(money){
       this.money -= money
       // this.$parent可以拿到父组件对象,操作父组件对象的数据
       this.$parent.money += money
    }

ref与$children区别

ref可以获取到当前组件的子组件
$children可以获取到当前组件的全部子组件(以数组形式,但其元素顺序不确定)

mixin

项目中出现很多结构类似的地方:想到组件复用
项目中很多组件的JS业务逻辑相似:想到mixin(可以把多个组件JS重复的部分封装成一个mixin
比如把儿子女儿给BABA钱这个操作封装在myMixin.js中👇
(除了methods,还可以封装mountedcomputed等)

//定义混入
export default {
  methods: {
    giveMoney(money){
      this.money -= money
      // this.$parent可以拿到父组件对象,操作父组件对象的数据
      this.$parent.money += money
    },
  },
  mounted(){
    console.log(111234)
  }
}

子组件👇

<template>
  <div style="background: #ccc; height: 50px;">
    <h3>儿子小明: 有存款: {{money}}</h3>
    <button @click="giveMoney(50)">BABA: 50</button>
  </div>
</template>

<script>
import myMixin from '@/pages/Communication/ChildrenParentTest/myMixin'
export default {
  name: 'Son',
  mixins:[myMixin],
  data () {
    return {
      money: 30000
    }
  },
</script>

插槽

可以实现父子组件通信
默认插槽
具名插槽
作用域插槽:子组件的数据来源于父组件,且子组件决定不了自身的结构与样式

作用域插槽完成的事情:

  父子之间通信

  1\ 数据是在父组件当中的
  2\ 数据最终传递给了子组件进行展示v-for
  3\ 子组件在展示数据的过程中,数据的结构是由父组件说了算的

父组件👇

    <h2>效果一: 显示TODO列表时, 已完成的TODO为绿色</h2>
    <List :todos="todos">
      <template slot-scope="scopeProps"> 
        <!--  scopeProps最终是一个对象,接收子组件作用域插槽回传给父组件的数据组成的对象-->
       <span :style="{color:scopeProps.todo.isComplete?'green':'black'}">{{scopeProps.todo.text}}</span>
      </template> 
    </List> 
    <hr>
    <h2>效果二: 显示TODO列表时, 带序号, TODO的颜色为蓝绿搭配</h2>
    <List :todos="todos">
      <template slot-scope="{todo,index}">
        <h2 :style="{color:index % 2===0?'blue':'green'}"><span>{{index+1}}</span>{{todo.text}}</h2>
      </template>
    </List>

子组件👇

<template>
  <ul>
    <li v-for="(todo, index) in todos" :key="index">
      <!-- :todo="todo" 不是props组件通信,作用域插槽通信 :把数据回传给父组件-->
      <slot :todo="todo" :index="index">{{todo.text}}</slot>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'List',
  props: {
    todos: Array
  }
}
</script>

插槽在使用第三方组件库时会经常用到


版权声明:本文为weixin_44286392原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。