Vue组件数据通信方案总结

我心飞翔 分类:vue

背景

初识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”>我给父组件赋值</ b​​utton>

</ 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组件赋值</ b​​utton>

</ div>

</ template>

<脚本>

导出默认值{

名称:“ demo1”,

data(){

返回{

名称:“ xxx”

};

},

道具: {

事件

},

方法: {

send(){

this.Event。$ emit(“ message-a”,this.name);

}

}

};

</ script>

//子组件2

<模板>

<div class =“ center”>

2.我的年龄是:{{age}}岁

<button @ click =“ send”>我给3组件赋值</ b​​utton>

</ 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

大家可以根据自己的使用场景选择不同的通信方式,当然还是都自己写写代码,试验一把来的印象深刻喽。

回复

我来回复
  • 暂无回复内容