【Vue2】组件通信方式有哪些?

vue组件间通信方式: props$emit$refsEventBus$parent/$children$attrs/$listenersprovide/injectvuex

组件之间的关系大致归类:

  • 父子组件通信
  • 非父子组件之间的通信(兄弟组件、隔代关系组件等)

1、props / $emit

1.1 父组件传子组件props

  • 子组件设置props属性,定义接受父组件传递过来的参数。
  • 父组件在使用子组件的标签中通过字面量来传递值。
// Father.vue
<Children name='jack' age=20 />
// Children.vue
// props:['name','age']
props:{
  name: String,
  age:{
    type: Number,   // 数据类型
    require: true,  // 是否必须传递
    defaule: 18     // 可设默认值
  }
}

:::details 查看示例
Father.vue 父组件

<template>
  <div id="app">
    <Children :users="users"></Children>
  </div>
</template>
<script>
import Children from "./components/Children"
export default {
  name: 'App',
  data(){
    return{
      users:["三儿","四儿","五儿"]
    }
  },
  components:{ Children }
}
</script>

Children.vue 子组件

<template>
  <div>
    <ul>
      <li v-for="user in users">{{user}}</li>//遍历传递过来的值,然后呈现到页面
    </ul>
  </div>
</template>
<script>
export default {
  name: 'Children',
  props:{
    users:{   // 这个就是父组件中子标签自定义名字
      type:Array, // 接受的数据类型
      required:true // 是否必须传递
    }
  }
}
</script>

:::

1.2 子组件传父组件$emit

  • 语法:this.$emit(eventNamem [...age])
  • 子组件通过 $emit 触发自定义事件(eventName),第二个参数为传递的数据。
  • 父组件绑定监听器获取子组件传递过来的参数。
//  
this.$emit('add', date)

// Father.vue
<Children @add='addDate($event)'>

::: details 查看示例

Children.vue 子组件

<template>
  <div>
    <h1 @click="changeTitle">{{title}}</h1>
  </div>
</template>
<script>
export default {
  name: 'Children',
  data() {
    return {
      title:"Vue.js Demo"
    }
  },
  methods:{
    changeTitle() {
      this.$emit("titleChanged","子向父组件传值");
    }
  }
}
</script>

Father.vue 父组件

<template>
  <div id="app">
    <Children @titleChanged="updateTitle" ></Children>
      //与子组件titleChanged自定义事件保持一致,updateTitle($event)接受传递过来的文字
    <h2>{{title}}</h2>
  </div>
</template>
<script>
import Children from "./components/Children"
export default {
  name: 'App',
  data(){
    return{
      title:"传递的是一个值"
    }
  },
  methods:{
    updateTitle(e){   //声明这个函数
      this.title = e;
    }
  },
  components:{ Children }
}
</script>

:::

2、$refs / $parent

::: tip

  • 这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据
  • 弊端是:无法在跨级或兄弟间通信。
    :::

2.1 ref / $refs

  • 父组件在子组件上设置 refName<Children ref='foo'>
  • 父组件通过$refs获得子组件的数据:this.$refs.[refName]

::: details 查看示例

// 子组件
export default {
  data(){
    return{
      title:"子组件 Data"
    }
  },
  methods:{
    getDate(){   //声明这个函数
      console.log('子组件 getDate')
    }
  }
}

// 父组件
<template>
  <children ref="son"></children>
</template>
<script>
  export default {
    ...
    ...
    mounted () {
      // 获取子组件的属性
      console.log(this.$refs.son.title);

      // 获取子组件的方法
      this.$refs.son.getDate()
    }
  }
</script>

:::

2.2 $parent / $children

::: tip
官方警示:节制使用,它们的主要目的是作为访问组件的应急方法。更推荐使用propsevents实现父子组件通信
:::

  • this.$parent可直接访问该组件的父实例或组件
  • 父组件也可以通过this.$children访问它所有的子组件。需注意它,返回的是一个子组件的集合(并不保证顺序,也不是响应式的)。想获得哪个组件的属性和方法可以通过:this.$children[index].子组件属性/方法

::: details 查看示例
parent.vue

<template>
  <div>
    <children-a></children-a>
    <children-b></children-b>
  </div>
</template>
<script>
import ChildrenA from "./components/ChildrenA"
import ChildrenB from "./components/ChildrenB"
export default {
  name: 'App',
  data(){
    return{
      title:"父组件title"
    }
  },
  components:{ ChildrenA, ChildrenB },
  mounted(){
    // 通过 $children 来获取子组件的属性/方法
    this.$children[0].getChildren();  // Children-A组件
    console.log(this.$children[0].msg);  // ChildrenA
    console.log(this.$children[1].msg);  // ChildrenB
  },
  methods:{
    getQuery(e){  // 供子组件访问
      console.log('parent', e)
    }
  }
}
</script>

Children-A/B.vue

// A.vue
<template>
  <div>
   <button @click='parentClik'>访问父组件</button>
  </div>
</template>
<script>
export default{
  data(){
    return {
      msg: 'ChildrenA'
    }
  },
  methods:{
    parentClick(){
      this.$parent.getQuery('AAA')  // 调用父组件内的方法

      console.log(this.$parent.title) // 获取父组件的属性
    },
    getChildren(){
      console.log('Chlidren-A组件')
    }
  }
}
</script>


// B.vue
<template>
  <div>
    ChildrenB
  </div>
</template>
<script>
export default{
  data(){
    return {
      msg: 'ChildrenB'
    }
  }
}
</script>

:::

$root$parent都能实现访问父组件的属性和方法,二者区别在于:如果存在多级子组件,通过parent访问得到的是它最近一级的父组件,通过root访问得到的是根父组件

3、EventBus

eventBus又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念。就像是所有组件公用相同的事件中心,可以向该中心注册发送事件或接收事件,所有组件都可以通知其他组件。参考地址

这种方法适用于小项目,项目复杂时会难以维护,这时推荐使用Vuex

  • 使用场景:兄弟组件传值
  • 创建一个中央时间总线EventBus
  • 兄弟组件通过$emit触发自定义事件,$emit第二个参数为传递的数值
  • 另一个兄弟组件通过$on监听自定义事件
// main.js
Vue.prototype.$bus = new Vue()

// A.vue  发送消息
this.$bus.$emit('foo', 'A页面的信息')

// B.vue  监听接受消息
this.$bus.$on('foo', (msg) => { console.log(msg)})

可以使用 this.$bus.$off('sendMsg') 来移除应用内所有对此某个事件的监听(beforeDestroy())。

或者直接调用 this.$bus.$off() 来移除所有事件频道,不需要添加任何参数 。

4、$attrs/$listeners

  • 使用场景:多级嵌套组件传递数据(祖先组件传递数据到子孙组件)
  • $attrs:
    • 包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (classstyle 除外)。
    • 当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (classstyle 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
  • $listeners
    • 包含了父作用域中的 (不含 .native 修饰符) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件。
    • 它是一个对象,里面包含了作用在这个组件上的所有事件监听器,相当于子组件继承了父组件的事件。

4.1 $attrs

// child:并未在props中声明foo  
<p>{{$attrs.foo}}</p>  
  
// parent  
<HelloWorld foo="foo"/>  

4.2 $listeners

// 给Grandson隔代传值,communication/index.vue  
<Child2 msg="lalala" @some-event="onSomeEvent"></Child2>  
  
// Child2做展开  
<Grandson v-bind="$attrs" v-on="$listeners"></Grandson>  
  
// Grandson使⽤  
<div @click="$emit('some-event', 'msg from grandson')">  
{{msg}}  
</div>  

::: details 查看示例
父组件

<template>
  <div>
    <Test :table="table" :sonVal="sonVal" :title="title" @getData="getData" @handleVal="handleVal" />
  </div>
</template>
<script>
import Test from './test.vue';
export default {
  data() {
    return {
      title: '组件props',
      table: [
        { a1: '11', b1: '22' },
        { a2: '11', b2: '22' }
      ],
      sonVal: 10
    };
  },
  components: { Test },
  methods: {
    getData() {
      console.log(this.title);
    },
    handleVal() {
      // this.table.push({ a3: '11', b3: '22' });
      this.sonVal = this.sonVal + 5;
    }
  }
};
</script>

子组件1

<template>
  <div>
    <h1>Test 组件: {{ title }}</h1>
    <!-- 通过 $attrs(属性,除了【props中定义的属性】) -->
    <!-- $listeners(方法)来给嵌套子组件传递父组件的属性和方法 -->
    <son v-bind="$attrs" v-on="$listeners" />
  </div>
</template>
<script>
// 引入子子组件
import son from './son';
export default {
  props: ['title'],
  components: {
    son
  },
  created() {
    console.log(this.$attrs); // {status: false}
    console.log(this.$listeners); // {getData: ƒ}
  }
};
</script>

嵌套子组件

<template>
  <div>
    <!-- 使用$attrs渲染数据 -->
    <h1>Son 组件 {{ $attrs.table }}</h1>
    <!-- $listeners分别调用绑定的方法 -->
    <h1> {{ $attrs.sonVal }} <el-button @click="$listeners.handleVal">val+5</el-button>
    </h1>

  </div>
</template>
<script>
export default {
  created() {
    this.$emit('getData', 'Son组件的传值'); // 也可在此使用$emit调用方法
    // console.log(this.$attrs) //{status: false}
    // console.log(this.$listeners) // {getData: ƒ}
  }
};
</script>

简单来说:$attrs$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners 里存放的是父组件中绑定的非原生事件。
:::

5、provide/inject

可以很轻松的实现跨级访问组件数据,API详情见provide/inject

  • 在祖先组件定义 provide 属性,返回传递的值;是一个对象或返回一个对象的函数
  • 在后代组件通过 inject 接收组件传递过来的值;是一个字符串数组或一个对象
// 祖先组件
export default {
  provide: {
    name: '测试'
  }
}

// 后代组件
export default {
  inject: ['name'], // 获取到祖先组件传递过来的值  
  created () {
    console.log(this.name);  // 测试
  }
}

:::tip
provideinject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
:::

6、Vuex

  • 适用于业务逻辑复杂,很多组件之间需要同时处理一些公共的数据
  • 作用相当于一个用来存储共享变量的容器 官链Vuex
  • state 用来存放共享变量的地方
  • getter 可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值
  • mutations 用来存放修改 state 的方法。
  • actions 也是用来存放修改 state 的方法,不过 action 是在 mutations 的基础上进行。常用来做一些异步操作

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值