背景
初识Vue.js,了解到组件是Vue的主要构成部分,但组件内部的作用域是相对独立的部分,组件之间的关系一般如下图:
组件A与组件B,C之间是父子组件,组件B,C之间是兄弟组件,而组件A,D之间是隔代的关系。
那么对于这些不同的关系,此处主要分享了他们之间可以采用几种数据通信方式,例如道具,$ emit / $ on,Vuex等,大家可以根据自己的使用场景可以选择合适的使用方式。
一,道具/ $ emit
1,Prop是你可以在组件上注册的一些自定义特性。当一个值传递给一个Prop特性的时候,它就变成了那个组件实例的一个属性。父组件向子组件传值,通过绑定属性来向子组件预计数据,子组件通过道具属性获取对应数据。
//父组件
<模板>
<div class =“ container”>
<child:title =“ title”> </ child>
</ div>
</ template>
<脚本>
从“ ./component/child.vue”导入Child;
导出默认值{
名称:“演示”,
数据:function(){
返回{
标题:“我是父组件给的”
};
},
组件: {
儿童
},
};
</ script>
//子组件<template>
<div class =“ text”> {{title}} </ div>
</ template>
<脚本>
导出默认值{
名称:“ demo”,
数据:function(){},
道具: {
标题:{
类型:字符串
}
},
};
</ script>
2,$ emit子组件向父组件传递值(通过事件形式),子组件通过$ emit事件向父组件发送消息,将自己的数据传递给父组件。
//父组件
<模板>
<div class =“ container”>
<div class =“ title”> {{title}} </ div>
<child @ changeTitle =“ parentTitle”> </ child>
</ div>
</ template>
<脚本>
从“ ./component/child.vue”导入Child;
导出默认值{
名称:“演示”,
数据:function(){
返回{
标题:null
};
},
组件: {
儿童
},
方法: {
parentTitle(e){
this.title = e;
}
}
};
</ script>
//子组件
<模板>
<div class =“ center”>
<button @ click =“ childTitle”>我给父组件赋值</ button>
</ div>
</ template>
<脚本>
导出默认值{
名称:“ demo”,
data(){
返回{
密钥:1
};
},
方法: {
childTitle(){
this。$ emit(’changeTitle’,`我给父组件的第$ {this.key}次`));
this.key ;
}
}
};
</ script>
小总结:常用的数据传输方式,父子间传递。
二,$ emit / $ on
这个方法是通过创建了一个空的vue实例,当做$ emit事件的处理中心(事件总线),通过他来触发以及监听事件,方便的实现了任意组件间的通信,包含父子,兄弟,隔代组件。
//父组件
<模板>
<div class =“ container”>
<child1:Event =“ Event”> </ child1>
<child2:Event =“ Event”> </ child2>
<child3:Event =“ Event”> </ child3>
</ div>
</ template>
<脚本>
从“ vue”导入Vue;
从“ ./component/child1.vue”导入Child1;
从“ ./component/child2.vue”导入Child2;
从“ ./component/child3.vue”导入Child3;
const Event = new Vue();
导出默认值{
名称:“演示”,
数据:function(){
返回{
事件:事件
};
},
组件: {
儿童1
儿童2
儿童3
},
};
</ script>
//子组件1
<模板>
<div class =“ center”>
1.我的名字是:{{name}}
<button @ click =“ send”>我给3组件赋值</ button>
</ div>
</ template>
<脚本>
导出默认值{
名称:“ demo1”,
data(){
返回{
名称:“ xxx”
};
},
道具: {
事件
},
方法: {
send(){
this.Event。$ emit(“ message-a”,this.name);
}
}
};
</ script>
//子组件2
<模板>
<div class =“ center”>
2.我的年龄是:{{age}}岁
<button @ click =“ send”>我给3组件赋值</ button>
</ div>
</ template>
<脚本>
/ *禁用eslint * /
导出默认值{
名称:“ demo2”,
data(){
返回{
年龄:“ 3”
};
},
道具: {
事件
},
方法: {
send(){
this.Event。$ emit(“ message-b”,this.age);
}
}
};
</ script>
//子组件3
<模板>
<div class =“ center”>我的名字是{{name}},今年{{age}}岁</ div>
</ template>
<脚本>
导出默认值{
名称:“ demo3”,
data(){
返回{
名称: ”,
年龄:”
};
},
道具: {
事件
},
Mounted(){
this.Event。$ on(’message-a’,name => {
this.name =名称;
});
this.Event。$ on(’message-b’,age => {
这个年龄=年龄;
});
},
};
</ script>
小总结:优化的在父子,兄弟,隔代组件中都可以互相数据通信。
三,Vuex
Vuex [1] 是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex实现了一个单项数据流,通过创建一个单个的状态数据,组件想要修改State数据只能通过Mutation来进行,例如页面上的操作想要修改State数据时,需要通过Dispatch(触发Action),而动作也不能直接操作数据,还需要通过Mutation来修改State中数据,最后根据State中数据的变化,来渲染页面。
// index.js
从’vue’导入Vue;
从’./index.vue’导入Tpl;
从“ ./store”导入商店;
新Vue({
商店,
渲染:h => h(Tpl),
})。$ mount(’#app’);
//储存
从’vue’导入Vue;
从’vuex’导入Vuex;
Vue.use(Vuex);
const store = new Vuex.Store({
状态:{
数:1
},
突变:{
增量(状态){
state.count ;
},
reduce(state){
state.count–;
}
},
动作:{
actIncrement({commit}){
commit(’increment’);
},
actReduce({commit}){
commit(’reduce’);
}
},
吸气剂:{
doubleCount:状态=> state.count * 2
}
});
导出默认存储;
// vue文件
<模板>
<div class =“ container”>
<p>我的count:{{count}} </ p>
<p> doubleCount:{{doubleCount}} </ p>
<button @ click =“ this.actIncrement”>增加</ button>
<button @ click =“ this.actReduce”>减少</ button>
</ div>
</ template>
<脚本>
从“ vuex”导入{mapGetters,mapActions,mapState};
导出默认值{
名称:“演示”,
数据:function(){
返回{};
},
组件: {},
道具: {},
计算值:{
… mapState([“ count”]),
… mapGetters([“ doubleCount”])
},
方法: {
… mapActions([[“ actIncrement”,“ actReduce”])
}
};
</ script>
Vuex中需要注意的点:
突变:是修改状态数据的唯一推荐方法,并且只能进行同步操作。
Getter:Vuex允许在Store中定义“ Getter”(该Store的计算属性)。Getter的返回值会根据他的依赖进行缓存,只有依赖值发生了变化,才会重新计算。
本段只是简单介绍了一下Vuex的运行方式,更多功能例如Module Module请参考官网[2]。
小总结:统一的维护了一份共同的状态数据,方便组件间共同调用。
四,$ attrs / $ listeners
Vue组件间传输数据在Vue 2.4版本后有新方法。除了道具外,还有了$ attrs / $ listeners。
•$ attrs:包含了父作用域中不作为Prop被识别(并且获取)的特性绑定(类和样式除外)。当一个组件没有声明任何Prop时,这里会包含所有父作用域的绑定( Class和Style除外),并且可以通过v-bind =“ $ attrs”内置内部组件-在创建高等级的组件时非常有用。
•$ listeners:包含了父作用域中的(排除.native修饰器的)v-on事件监听器。它可以通过v-on =“ $ listeners”内置内部组件
下面来看个例子
//父组件
<模板>
<div class =“ container”>
<button style =“ backgroundColor:lightgray” @ click =“ reduce”>减dd </ button>
<child1:aa =“ aa”:bb =“ bb”:cc =“ cc”:dd =“ dd” @ reduce =“ reduce”> </ child1>
</ div>
</ template>
<脚本>
从’./component/child1.vue’导入Child1;
导出默认值{
名称:“ demo”,
数据:function(){
返回{
aa:1
bb:2
抄送:3,
日:100
};
},
组件: {
儿童1
},
方法: {
减少() {
this.dd–;
}
}
};
</ script>
//子组件1
<模板>
<div>
<div class =“ center”>
<p> aa:{{aa}} </ p>
<p> child1的$ attrs:{{$$ attrs}} </ p>
<button @ click =“ this.reduce1”>组件1减dd </ button>
</ div>
<child2 v-bind =“ $ attrs” v-on =“ $ listeners”> </ child2>
</ div>
</ template>
<脚本>
从’./child2.vue’导入child2;
导出默认值{
名称:“ demo1”,
data(){
返回{};
},
道具: {
aa:数字
},
组件: {
小孩2
},
方法: {
reduce1(){
$ emit(’reduce’);
}
}
};
</ script>
//子组件2
<模板>
<div>
<div class =“ center”>
<p> bb:{{bb}} </ p>
<p> child2的$ attrs:{{$$ rss}} </ p>
<button @ click =“ this.reduce2”>组件2减dd </ button>
</ div>
<child3 v-bind =“ $ attrs”> </ child3>
</ div>
</ template>
<脚本>
从’./child3.vue’导入child3;
导出默认值{
名称:“ demo1”,
data(){
返回{};
},
道具: {
bb:数字
},
组件: {
小孩3
},
方法: {
reduce2(){
$ emit(’reduce’);
}
}
};
</ script>
//子组件3
<模板>
<div class =“ center”>
<p> child3的$ attrs:{{$$ attrs}} </ p>
</ div>
</ template>
<脚本>
导出默认值{
名称:“ demo3”,
data(){
返回{};
},
道具: {
dd:字符串
},
};
</ script>
简单来说,$ attrs里存放的是父组件中绑定的非道具属性,$ listeners里面存放的是父组件中绑定的非原生事件。
小总结:当传输数据,方法中断时,无需一一填写的小技巧。
五,提供者/注入
Vue 2.2版本以后添加了这两个API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,而且组件层次有多深,并在其上下游关系建立的时间里始终执行。简单来说,就是父组件通过Provider进行变量,任意子孙组件通过Inject来拿到变量。
//父组件
<模板>
<div class =“ container”>
<button @ click =“ this.changeName”>我要改名字了</ button>
<p>我的名字:{{name}} </ p>
<child1> </ child1>
</ div>
</ template>
<脚本>
从’./component/child1.vue’导入Child1;
导出默认值{
名称:“ demo”,
数据:function(){
返回{
名称:“ xxx”
};
},
//提供(){
//返回{
//名称:this.name //这种绑定方式是不可响应的
//};
//},
提供(){
返回{
obj:这个
};
},
组件: {
儿童1
},
方法: {
更换名字() {
this.name =’web前端’;
}
}
};
</ script>
//子组件
<模板>
<div>
<div class =“ center”>
<!-<p>子组件名字:{{name}} </ p>->
<p>子组件名字:{{this.obj.name}} </ p>
</ div>
<child2> </ child2>
</ div>
</ template>
<脚本>
从’./child2.vue’导入child2;
导出默认值{
名称:“ demo1”,
data(){
返回{};
},
道具: {},
//注入:[“ name”],
注入:{
obj:{
默认值:()=> {
返回{};
}
}
},
组件: {
小孩2
},
};
</ script>
需要注意的是:Provide and Inject绑定本质可响应的。这是刻意为之的。然而,如果您指出了一个可监听的对象,那么其对象的属性还是可响应的。
所以,如果采用的是我代码中注释的方式,父级的名称如果改变了,子组件this.name是不会改变的,而当采用代码中插入一个监听对象,修改对象中属性值,是可以监听到修改的。
提供者/注入在项目中需要有公共公共传参时使用还是颇为方便的。
小总结:传输数据父级一次注入,子孙组件一起共享的方式。
六,$ parent / $ children&$ refs
•$ parent / $ children:指定已创建的实例之父实例,在两者之间建立父子关系。子实例可以使用this。$ parent访问父实例,子实例被推入父实例的$ children嵌套中。
•$ refs:一个对象,持有注册过ref特性[3] 的所有DOM元素和组件实例。ref被使用给元素或子组件注册引用信息。引用信息注册在父组件的$ refs对象上。如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件。
//父组件
<模板>
<div class =“ container”>
<p>我的标题:{{title}} </ p>
<p>我的名字:{{name}} </ p>
<child1 ref =“ comp1”> </ child1>
<child2 ref =“ comp2”> </ child2>
</ div>
</ template>
<脚本>
从’./component/child1.vue’导入Child1;
从’./component/child2.vue’导入Child2;
导出默认值{
名称:“ demo”,
数据:function(){
返回{
标题:null,
名称:null,
内容:’就是我’
};
},
组件: {
儿童1
儿童2
},
Mounted(){
const comp1 = this。$ refs.comp1;
this.title = comp1.title;
comp1.sayHello();
this.name = this。$ children [1] .title;
},
};
</ script>
//子组件1-ref方式
<模板>
<div>
<div class =“ center”>我的父组件是谁:{{content}} </ div>
</ div>
</ template>
<脚本>
导出默认值{
名称:“ demo1”,
data(){
返回{
标题:“我是子组件”,
内容:null
};
},
Mounted(){
this.content = this。$ parent.content;
},
方法: {
问好() {
window.alert(’Hello’);
}
}
};
</ script>
//子组件2-children方式
<模板>
<div>
<div class =“ center”> </ div>
</ div>
</ template>
<脚本>
导出默认值{
名称:“ demo1”,
data(){
返回{
标题:“我是子组件2”
};
},
};
</ script>
通过示例可以看到这两种方式都可以父子间通信,而缺点也很统一,就是都不能跨级以及兄弟间通信。
小总结:父子组件间共享数据以及方法的便捷实践之一。
总结
组件间不同的使用场景可以分为3类,对应的通信方式如下:
•父子通信:道具/ $ emit,$ emit / $ on,Vuex,$ attrs / $ listeners,提供/注入,$ parent / $ children&$ refs
•兄弟通信:$ emit / $ on,Vuex
•隔代(跨级)通信:$ emit / $ on,Vuex,提供/注入,$ attrs / $ listeners
大家可以根据自己的使用场景选择不同的通信方式,当然还是都自己写写代码,试验一把来的印象深刻喽。