一. 通信方式列表
props
vue定义事件
全局事件总线
v-model
.sync
$attr与$linteners
$ref & $children & $parent
provide与inject
vuex
插槽 ===> 作用域插槽
根据通信的两个组件间关系选择通信方式
// 父--->子
props
v-model
.sync
$attrs
$refs
$children
插槽 ===> 作用域插槽slot-scope
// 子--->父
props
vue自定义事件
.sync
$listeners
$parent
// 祖--->孙
provide与inject
// 兄弟或任意
全局事件总线 ===> 消息订阅与发布PubSub
vuex
二. 不同通信方式的使用方式
1. props
实现 父—>子 通信:属性值是非函数
实现 子—>父 通信:属性值是函数
props 是最基本,应用最多的一种通信方式
props是出现在子组件中用来接收或者调用父组件传递过来的函数
使用方法
// 父组件
// 首先要定义一个 list 属性储存数据
// 定义一个绑定属性 mlist 为后续 props 做铺垫
<template>
<div>
<child id="1"
:openReading="article.openReading"
:title="article.title"
:tags="article.tags"
:author="article.author"
/>
</div>
</template>
<script>
import Child from './components/Child'
export default {
components: { Child },
data () {
return {
article: {
id: 1,
openReading: true,
title: '雨林博客园',
tags: ['name', 'age'],
author: {
name: 'Lin'
}
}
}
}
}
</script>
// 子组件
<template>
<div>
<div>{{id}} - {{openReading}} - {{title}} - {{tags}}-{{author.name}}</div>
</div>
</template>
<script>
export default {
props: ['id', 'openReading', 'title', 'tags', 'author']
}
</script>
子向父建议使用自定义事件,这里用props的子向父传函数形式不做演示
2.vue自定义事件
用来实现子向父通信
element-ui该组件的事件监听语法用的都是自定义事件,项目当中用到自定义事件的地方也不少
使用方法
// 父组件中绑定自定义事件监听:
<Child @eventName = "callback($event)">
// 子组件中分发事件
this.$emit('eventName',data)
3.全局事件总线 ===> 消息订阅与发布
用来实现任意组件通信
本质是一个对象,所有组件对象都可以看到他,可以用 $on 和 $emit 方法
在前台项目中使用全局事件总线较多,但是并不推荐使用,有性能问题
使用方法
// 将入口 js 中的 vm 作为全局事件总线对象
beforeCreate() {
Vue.prototype.$bus = this
}
// 分发事件/传递数据的组件:
this.$bus.$emit('eventName',data)
// 处理事件/接收数据的组件:
this.$bus.$on('eventName',(data)={})
4. v-model
用来实现父子之间互相通信(语法糖)
组件标签上的 v-model 本质:动态 value 属性与自定义 input 监听来接收子组件分发的数据来更新父组件的数据
element-ui中的表单项相关组件都用了v-model: Input / Select / Checkbox / Radio
使用方法
// 父组件
<CustomInput v-model="name"/>
<!-- 等价于 -->
<CustomInput :value="name" @input="name=$event"/>
// 子组件
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
props: ['value']
5. .sync
实现父子组件互相通信/同步(在原本父向子的基础上增加子向父)
组件标签的属性上使用 .sync 的本质:通过事件监听来接收子组件分发过来的数据并更新父组件的数据
element-ui在有显示隐藏的组件上: Dialog / Drawer
// 父组件:
<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']
6. $attrs 与 $listeners
- $ attrs
实现当前组件的父组件向当前组件的子组件通信
它是包含所有父组件传入的标签属性(排除props声明, class与style的属性)的对象
使用: 通过 v-bind=“$attrs” 将父组件传入的n个属性数据传递给当前组件的子组件
2). $ listeners
实现当前组件的子组件向当前组件的父组件通信
l i s t e n e r s 是包含所有父组件传入的自定义事件监听名与对应回调函数的对象使用 : 通过 v − o n = " listeners是包含所有父组件传入的自定义事件监听名与对应回调函数的对象 使用: 通过v-on=" listeners是包含所有父组件传入的自定义事件监听名与对应回调函数的对象使用:通过v−on="listeners" 将父组件绑定给当前组件的事件监听绑定给当前组件的子组件
应用: 利用它封装了一个自定义的带hover文本提示的el-button
7.$refs & $children & $parent
- $ refs
实现父组件向指定子组件通信
$ refs是包含所有有ref属性的标签对象或组件对象的容器对象
使用: 通过 this.$refs.child 得到子组件对象, 从而可以直接更新其数据或调用其方法更新数据 - $ children
实现父组件向多个子组件通信
$ children是所有直接子组件对象的数组
使用: 通过this.$children 遍历子组件对象, 从而可以更新多个子组件的数据 - $ parent
实现子组件向父组件通信
$ parent是当前组件的父组件对象
使用: 通过this.$ parent 得到父组件对象, 从而可以更新父组件的数据
应用: 在后台管理项目中使用了$ refs
8.provide与inject
- 实现祖孙组件间直接通信
- 使用
在祖组件中通过provide配置向后代组件提供数据
在后代组件中通过inject配置来声明接收数据 - 注意:
不太建议在应用开发中使用, 一般用来封装vue插件
provide提供的数据本身不是响应式的 ==> 父组件更新了数据, 后代组件不会变化
provide提供的数据对象内部是响应式的 ==> 父组件更新了数据, 后代组件也会变化
应用: element-ui中的Form组件中使用了provide和inject
9. Vuex
- 实现任意组件间通信
- Vuex 是一个专为 Vue 应用程序设计的管理多组件共享状态数据的 Vue 插件
任意组件都可以读取到Vuex中store的state对象中的数据
任意组件都可以通过dispatch()或commit()来触发store去更新state中的数据
一旦state中的数据发生变化, 依赖于这些数据的组件就会自动更新
应用: 前台和后台项目都有用vuex管理组件数据
10. 插槽 ==> 作用域插槽slot-scope
- 实现父组件向子组件传递标签内容
- 什么情况下使用作用域插槽?
父组件需要向子组件传递标签结构内容
但决定父组件传递怎样标签结构的数据在子组件中 - 应用: element-ui中的Table组件
子组件:
<slot :row="item" :$index="index"> <!-- slot的属性会自动传递给父组件 -->
</slot>
父组件:
<template slot-scope="{row, $index}">
<span>{{$index+1}}</span>
<span :style="{color: $index%2===1 ? 'blue' : 'green'}" >{{row.text}}</span>
</template>
11.消息订阅与发布PubSub
- 一种组件通信方式,适用于任何组件间通信
- 在小程序,react当中没有全局使劲按总线,pubsub更多应用在react和小程序当中
- 使用步骤:
1.安装npm install --save pubsub-js
2.引入:import pubsub from “pubsub-js”,在要使用的组件中引入pubsub-js,这个不能全局引入只能哪个组件中使用就在哪个组件中引入
3.A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
4.最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。
在a组件中订阅消息
<template>
<div>
<h1>当前年龄为{{age}}</h1>
<Son/>
</div>
</template>
<script>
import Son from './components/Son.vue'
import pubsub from 'pubsub-js'
export default {
name: 'App',
data() {
return {
age: 18
}
},
components: {
Son
},
methods: {
add (age) {
this.age += age
}
},
mounted() {
// 订阅消息
this.pubId = pubsub.subscribe('add', (msgName, data) => {
this.add(data)
})
},
beforeDestroy() {
// 在组件销毁之前 清除订阅消息
pubsub.unsubscribe(this.pubId)
},
}
</script>
<style>
</style>
在b组件中提供数据
<template>
<div class="school">
<p>我是子组件</p>
<button @click="demo">点击加1</button>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'School',
data() {
return {
number: 1
}
},
methods: {
demo() {
// 提供数据
pubsub.publish('add', this.number)
}
}
}
</script>
<style scoped>
.school{
background-color: skyblue;
padding: 5px;
}
</style>