Vue组件之间的通信方式都有哪些?
一、组件间通信的概念
首先,我们把组件间通信进行拆分
- 组件
- 通信
组件是vue最强大的功能之一,vue中每一个.vue文件我们都可以视为一个组件通信指的是发送者通过某种媒体以某种格式来传递信息到收信者以达到某个目的。广义上讲,任何信息的交通都是组件通信间通信即指组件(.vue)通过某种方式来传递信息以达到某个目的。
二、组件间通信解决了什么
在vue中,每个组件之间都有独自的作用域,组件间的数据是无法共享的但实际工作中我们常常需要让组件之间共享数据,这也是组件通讯的目的,让他们互相之间能进行通讯,这样才能构成一个有机的完整系统
三、组件间通信分类
组件间通讯的分类可以分为一下及部分
- 父子组件之间的通信
- 兄弟组件之间的通信
- 祖孙与后代组件之间的通信
- 非关系组件之间通信
四、组件间通信的方案
props/$emit适用于父子组件通信
父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件来做到的
上代码:
父组件传递数据给子组件,子组件设置props属性,接收父组件传递过来的参数
//父组件
<Children name="jack" age=18 />
//子组件
props:{
// 字符串形式
name:String // 接收的类型参数
// 对象形式
age:{
type:Number, // 接收的类型为数值
defaule:18, // 默认值为18
require:true // age属性必须传递
}
}
子组件通过$emit触发自定义事件, $emit第二个参数为传递的数值
父组件绑定监听器获取子组件传递过来的参数
//父组件
<Children @add="cartAdd($event)" />
//子组件
this.$emit('add', good)
(vue3废弃) ref与$parent/ $children适用于父组件通信
ref:如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例
$parent / $children:访问父组件的属性或方法 / 访问子组件的属性或方法
上代码:
//父组件
<template>
<div>
<h3>父组件</h3>
<button @click="setChildInfo">向子组件传值</button>
<hr />
<h3>子组件</h3>
<child ref="child"></child>
</div>
</template>
<script>
//子组件地址(仅供参考),具体以实际项目目录地址为准
import child from "./Child.vue";
export default {
components: {
child: child
},
data() {
return {};
},
methods: {
// 向子组件传值
setChildInfo() {
this.$refs.child.cInfo = "c2";
}
}
};
</script>
<style scoped>
</style>
//子组件
<template>
<div>
<p>收到父组件数据:{{ cInfo }}</p>
</div>
</template>
<script>
export default {
data() {
return {
cInfo: "c1"
};
}
};
</script>
<style scoped>
</style>
Event($emit / $on) 适用于父子、隔代、兄弟组件通信 (全局事件总线)
这种方法通过一个空的vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信
首先创建一个中央事件总线EventBus
兄弟组件通过$emit触发自定义事件, $emit第二个参数为传递的数值
另一个兄弟组件通过 $on监听自定义事件
代码如下:
//在main.js文件中
Vue.prototype.$bus = new Vue()
//组件A
this.$bus.$emit('bus','bus传值,组件B传过来的')
//组件B
this.$bus.$on('bus',(msg)=>{
console.log('msg',msg)
})
详细代码
//组件A
<template>
<div class="home"
<h3>eventBus</h3>
<button @click="send">发送数据</button>
</div>
</template>
<script>
export default{
name:'组件A名称',
data(){
return {
count:10
}
},
methods:{
send(){
this.$bus.$emit('send',this.count)
}
}
}
</script>
//组件B
<template>
<div class="about"
<h1>This is an about page</h1>
</div>
</template>
<script>
export default {
created(){
this.$bus.$on('send',val=>{
console.log(val)
})
}
}
</script>
(vue3废弃) $attrs / $ listeners 适用于隔代组件通信
$attrs:包含了父作用域不被prop所识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过**v-bind=" $attrs “**传入内部组件。通常配合inherit选项一起使用,多余的属性不会被解析到标签上
$listeners:包含了父作用域中的(不含 .native修饰器的)v-on事件监听器。它可以通过v-on=” $listeners"传入内部组件
我们假设:组件A包裹组件B,组件B包裹组件C
代码实例如下
//组件A
<template>
<div id="app">
<!-- 此处监听了事件,可以在C组件中直接触发 -->
<b-child
nameToB="nameToB"
nameToC="nameToC"
@buttonClick="buttonClick"
>
</b-child>
</div>
</template>
<script>
import BChild from "./B.vue";
export default {
data() {
return {};
},
components: { BChild },
methods: {
buttonClick() {
console.log("buttonClick...");
}
}
};
</script>
//组件B
<template>
<div>
<h1>B组件</h1>
<p>name: {{nameToB}}</p>
<p>$attrs: {{$attrs}}</p>
<hr>
<!-- C组件中能直接触发buttonClick的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
<!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
<c-child v-bind="$attrs" v-on="$listeners"></c-child>
</div>
</template>
<script>
import CChild from './C.vue';
export default {
props: ['nameToB'],
components: { CChild },
data () {
return {};
},
// inheritAttrs: false,
};
</script>
//组件C
<template>
<div>
<h1>C组件</h1>
<p>name: {{nameToC}}</p>
<p>$attrs: {{$attrs}}</p>
<button @click="buttonClick">点击C按钮</button>
</div>
</template>
<script>
export default {
// inheritAttrs: false,
props: ['nameToC'],
data () {
return {};
},
methods: {
buttonClick(){
this.$emit('buttonClick');
}
}
};
</script>
provide / inject 适用于隔代组件通信
祖先组件中通过provider 来提供变量,然后在子孙组件中通过inject来注入变量。provide / inject API主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系
- 在祖先组件定义provide属性,返回传递的值
- 在后代组件通过inject接收组件传递过来的值
代码实例:
//祖先组件
<template>
<div>
<Content></Content>
</div>
</template>
<script>
import Content from './components/Content.vue'
export default{
data(){
return{
message:'这里是中国,'
}
},
methods:{
changeMag:function(){
this.message='我所站立的地方!'
}
},
components:{
Content,
}
}
</script>
<style>
</style>
//父组件
<template>
<h2>Content组件:{{message}}</h2>
<button @click="message='你好'">改变值</button>
<hello></hello>
</template>
<script>
import hello from './hello.vue'
export default{
data(){
return{
message:'word',
obj:{
message:'hello word'
}
}
},
// provide:{
// message:'word'
// },
//访问组件实例
//provide/inject默认并不是响应式的
provide(){
return{
// message:this.message
// obj:this.obj//响应式对象
message:()=>this.message//函数返回响应式数据
}
},
components:{
hello
}
}
</script>
<style>
</style>
//子组件
<template>
<h2>我是hello组件</h2>
<!-- <h2>hello组件:{{obj.message}}</h2> -->
<h2>hello--{{newMsg}}</h2>
</template>
<script>
export default{
data(){
return{}
},
computed:{
newMsg:function(){
return this.message()
}
},
inject:['message']
// inject:['obj']//通过响应式对象
}
</script>
<style>
</style>
vuex 适用于父子、隔代、兄弟组件通信
vuex是一个专为vue.js应用程序开发的状态管理模式。每一个vuex应用的核心就是store(数据仓库)。store基本上就是一个容器,它包含着你的应用中大部分的状态(state)
vuex的状态存储是响应式的。当vue组件从store中读取状态的时候,若store中的状态发生了变化,那么相应的组件也会响应的得到高效更新
改变store中的状态的唯一途径就是显式的提交(commit)mutation。这样可以使我们可以方便地跟踪每一个状态地变化
适用于任何组件间地数据共享
-
state用来存放共享变量的地方
-
getter,可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值
-
mutations用来存放修改state的方法。
-
actions也是用来存放修改state的方法,不过action是在mutations的基础上进行。常用来做一些异步操作
代码:
const store = new Vuex.Store({
// 声明仓库数据
state: {
// 跟其他组件定义data一样
键:值,//
...
},
// 仓库数据过滤
getters: {
方法名(state) {
+return
},
...
},
// 更新仓库数据(这里面不允许写异步请求)
mutations: {
// 无参数
get/set方法名(state) {
},
// 有参数(思考:有几个形参,根据actions调用传递的实参定)
get/set方法名(state, data1, ..., datan) {
state.键 = 异步请求数据(data1)
}
},
// 更新仓库数据(这里面专门写异步请求,但是不能直接操作state 操作mutations)
actions: {
get/set方法名(context) {
//不传递参数:context.commit(mutations里面的方法名)
//传递参数: context.commit(mutations里面的方法名, 数据1,..,数据n)
}
}
})
//
//在组件中触发仓库里面数据语法
//如果在组件中调用state&getter:
computed: {
方法名() { // 推荐方法名就是vuex.state中的键名
return this.$store.state/getters.键
}
}
// 如何调用mutations
// 如何调用actions
methods: {
方法名() {
this.$store.commit(mutations里面方法名)
this.$store.dispatch(actions里面的方法名)
}
}
总结
- 父子关系的组件数据传递选择 props 与 $emit进行传递,也可选择ref
- 兄弟关系的组件数据传递可选择 b u s ,其次可以选择 bus,其次可以选择 bus,其次可以选择parent进行传递
- 祖先与后代组件数据传递可选择attrs与listeners或者 Provide与 Inject
- 复杂关系的组件数据传递可以通过vuex存放共享的变量