vue组件数据通信总结全面

背景:

初始Vue.js,了解组件时Vue的主要构成部分,但组件内部的作用域是相对独立的部分,组件之间的关系一般如下图:

 

组件A与组件B、C之间是父子组件,组件B、C之间是兄弟组件,而组件A、D之间是隔代的关系

那么对于这些不同的关系,本文主要分享了他们之间可以采用的几种数据通信方式。例如Props、$emit/$on、Vuex等。可以根据使用场景选择适合的使用方式。

一、Prop / $emit

1、Prop是你可以在组件上注册的一些自定义特性。当一个值传递给一个Prop特性的时候,它就变成了那个组件实例的一个属性。父组件向子组件传值,通过绑定属性来向子组件传入数据,子组件通过Props属性获取对应数据

//父组件
<template>
   <div>
         <child :title="title"></child>
    </div> 
</template>
<script>
import Child from './child.vue'
  export defalut{
      name:'parent',
      data(){
          return{
                 title:'我是父组件给的'
               }
    },
       components:{
          Child
     }
  }
</script>


//子组件
<template>
   <div>
      <p>{{title}}</p>
   </div>
</template>
<script>
  export default{
    name:'child',
    data(){
         return{

          }
     },
     props:['title']
  }
</script>

 2、$emit 子组件向父组件传值(通过事件形式),子组件通过 $emit 事件向父组件发送消息,将自己的数据传递给父组件。

 

 

  

 1 // 父组件
 2 <template>
 3   <div class="container">
 4     <div class="title">{{title}}</div>
 5     <child @changeTitle="parentTitle"></child>
 6   </div>
 7 </template>
 8 
 9 <script>
10 import Child from "./component/child.vue";
11 
12 export default {
13   name: "demo",
14   data: function() {
15     return {
16       title: null
17     };
18   },
19   components: {
20     Child
21   },
22   methods: {
23     parentTitle(e) {
24       this.title = e;
25     }
26   }
27 };
28 </script>
29  
30 
31 // 子组件
32 <template>
33   <div class="center">
34     <button @click="childTitle">我给父组件赋值</button>
35   </div>
36 </template>
37 
38 <script>
39 export default {
40   name: 'demo',
41   data() {
42     return {
43       key: 1
44     };
45   },
46   methods: {
47     childTitle() {
48       this.$emit('changeTitle', `我给父组件的第${this.key}次`);
49       this.key++;
50     }
51   }
52 };
53 </script>

小总结:常用的数据传输方式,父子间传递。

二、 $emit / $on

 

这个方法是通过创建了一个空的 vue 实例,当做 $emit 事件的处理中心(事件总线),通过他来触发以及监听事件,方便的实现了任意组件间的通信,包含父子,兄弟,隔代组件。

 

 

  1 // 父组件
  2 <template>
  3   <div class="container">
  4     <child1 :Event="Event"></child1>
  5     <child2 :Event="Event"></child2>
  6     <child3 :Event="Event"></child3>
  7   </div>
  8 </template>
  9 
 10 <script>
 11 import Vue from "vue";
 12 
 13 import Child1 from "./component/child1.vue";
 14 import Child2 from "./component/child2.vue";
 15 import Child3 from "./component/child3.vue";
 16 
 17 const Event = new Vue();
 18 
 19 export default {
 20   name: "demo",
 21   data: function() {
 22     return {
 23       Event: Event
 24     };
 25   },
 26   components: {
 27     Child1,
 28     Child2,
 29     Child3
 30   },
 31 };
 32 </script>
 33  
 34 
 35 // 子组件1
 36 <template>
 37   <div class="center">
 38     1.我的名字是:{{name}}
 39     <button @click="send">我给3组件赋值</button>
 40   </div>
 41 </template>
 42 
 43 <script>
 44 export default{
 45   name: "demo1",
 46   data() {
 47     return {
 48       name: "政采云"
 49     };
 50   },
 51   props: {
 52     Event
 53   },
 54   methods: {
 55     send() {
 56       this.Event.$emit("message-a", this.name);
 57     }
 58   }
 59 };
 60 </script>
 61  
 62 
 63 // 子组件2
 64 <template>
 65   <div class="center">
 66     2.我的年龄是:{{age}}岁
 67     <button @click="send">我给3组件赋值</button>
 68   </div>
 69 </template>
 70 
 71 <script>
 72 /* eslint-disable */
 73 export default {
 74   name: "demo2",
 75   data() {
 76     return {
 77       age: "3"
 78     };
 79   },
 80   props: {
 81     Event
 82   },
 83   methods: {
 84     send() {
 85       this.Event.$emit("message-b", this.age);
 86     }
 87   }
 88 };
 89 </script>
 90  
 91 
 92 // 子组件3
 93 <template>
 94   <div class="center">我的名字是{{name}},今年{{age}}岁</div>
 95 </template>
 96 
 97 <script>
 98 export default{
 99   name: 'demo3',
100   data() {
101     return {
102       name: '',
103       age: ''
104     };
105   },
106   props: {
107     Event
108   },
109   mounted() {
110     this.Event.$on('message-a', name => {
111       this.name = name;
112     });
113     this.Event.$on('message-b', age => {
114       this.age = age;
115     });
116   },
117 };
118 </script>

 

小总结:巧妙的在父子,兄弟,隔代组件中都可以互相数据通信。

三、 Vuex

 

Vuex[1] 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

 

 

  Vuex 实现了一个单项数据流,通过创建一个全局的 State 数据,组件想要修改 State 数据只能通过 Mutation 来进行,例如页面上的操作想要修改 State 数据时,需要通过 Dispatch (触发 Action ),而 Action 也不能直接操作数据,还需要通过 Mutation 来修改 State 中数据,最后根据 State 中数据的变化,来渲染页面。

 

 1 // index.js
 2 import Vue from 'vue';
 3 import Tpl from './index.vue';
 4 import store from './store';
 5 
 6 new Vue({
 7   store,
 8   render: h => h(Tpl),
 9 }).$mount('#app');
10  
11 
12 // store
13 import Vue from 'vue';
14 import Vuex from 'vuex';
15 
16 Vue.use(Vuex);
17 
18 const store = new Vuex.Store({
19   state: {
20     count: 1
21   },
22   mutations: {
23     increment(state) {
24       state.count++;
25     },
26     reduce(state) {
27       state.count--;
28     }
29   },
30   actions: {
31     actIncrement({ commit }) {
32       commit('increment');
33     },
34     actReduce({ commit }) {
35       commit('reduce');
36     }
37   },
38   getters: {
39     doubleCount: state => state.count*2
40   }
41 });
42 
43 export default store;
44  
45 
46 // vue文件
47 <template>
48   <div class="container">
49     <p>我的count:{{count}}</p>
50     <p>doubleCount:{{doubleCount}}</p>
51     <button @click="this.actIncrement">增加</button>
52     <button @click="this.actReduce">减少</button>
53   </div>
54 </template>
55 
56 <script>
57 import { mapGetters, mapActions, mapState } from "vuex";
58 
59 export default {
60   name: "demo",
61   data: function() {
62     return {};
63   },
64   components: {},
65   props: {},
66   computed: {
67     ...mapState(["count"]),
68     ...mapGetters(["doubleCount"])
69   },
70   methods: {
71     ...mapActions(["actIncrement", "actReduce"])
72   }
73 };
74 </script>

Vuex 中需要注意的点: 

Mutation :是修改State数据的唯一推荐方法,且只能进行同步操作。 

Getter :Vuex 允许在Store中定义 “ Getter”(类似于 Store 的计算属性)。Getter 的返回值会根据他的依赖进行缓存,只有依赖值发生了变化,才会重新计算。 

本段只是简单介绍了一下 Vuex 的运行方式,更多功能例如 Module 模块请参考官网[2] 。 

小总结:统一的维护了一份共同的 State 数据,方便组件间共同调用。

四、 $attrs / $listeners 

Vue 组件间传输数据在 Vue 2.4 版本后有了新方法。除了 Props 外,还有了 $attrs / $listeners。

• $attrs: 包含了父作用域中不作为 Prop 被识别 (且获取) 的特性绑定(Class 和  Style 除外)。当一个组件没有声明任何 Prop 时,这里会包含所有父作用域的绑定 (Class  Style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。  

• $listeners: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件  

下面来看个例子 

  1 // 父组件
  2 <template>
  3   <div class="container">
  4     <button style="backgroundColor:lightgray" @click="reduce">减dd</button>
  5     <child1 :aa="aa" :bb="bb" :cc="cc" :dd="dd" @reduce="reduce"></child1>
  6   </div>
  7 </template>
  8 
  9 <script>
 10 import Child1 from './component/child1.vue';
 11 export default {
 12   name: 'demo',
 13   data: function() {
 14     return {
 15       aa: 1,
 16       bb: 2,
 17       cc: 3,
 18       dd: 100
 19     };
 20   },
 21   components: {
 22     Child1
 23   },
 24   methods: {
 25     reduce() {
 26       this.dd--;
 27     }
 28   }
 29 };
 30 </script>
 31  
 32 
 33 // 子组件1
 34 <template>
 35   <div>
 36     <div class="center">
 37       <p>aa:{{aa}}</p>
 38       <p>child1的$attrs:{{$attrs}}</p>
 39       <button @click="this.reduce1">组件1减dd</button>
 40     </div>
 41     <child2 v-bind="$attrs" v-on="$listeners"></child2>
 42   </div>
 43 </template>
 44 
 45 <script>
 46 import child2 from './child2.vue';
 47 export default {
 48   name: 'demo1',
 49   data() {
 50     return {};
 51   },
 52   props: {
 53     aa: Number
 54   },
 55   components: {
 56     child2
 57   },
 58   methods: {
 59     reduce1() {
 60       this.$emit('reduce');
 61     }
 62   }
 63 };
 64 </script>
 65  
 66 
 67 // 子组件2
 68 <template>
 69   <div>
 70     <div class="center">
 71       <p>bb:{{bb}}</p>
 72       <p>child2的$attrs:{{$attrs}}</p>
 73       <button @click="this.reduce2">组件2减dd</button>
 74     </div>
 75     <child3 v-bind="$attrs"></child3>
 76   </div>
 77 </template>
 78 
 79 <script>
 80 import child3 from './child3.vue';
 81 export default {
 82   name: 'demo1',
 83   data() {
 84     return {};
 85   },
 86   props: {
 87     bb: Number
 88   },
 89   components: {
 90     child3
 91   },
 92   methods: {
 93     reduce2() {
 94       this.$emit('reduce');
 95     }
 96   }
 97 };
 98 </script>
 99  
100 
101 // 子组件3
102 <template>
103   <div class="center">
104     <p>child3的$attrs:{{$attrs}}</p>
105   </div>
106 </template>
107 
108 <script>
109 export default {
110   name: 'demo3',
111   data() {
112     return {};
113   },
114   props: {
115     dd: String
116   },
117 };
118 </script>
简单来说,$attrs 里存放的是父组件中绑定的非 props 属性,$listeners 里面存放的是父组件中绑定的非原生事件。

小总结:当传输数据、方法较多时,无需一一填写的小技巧。

五、 Provider / Inject

 Vue 2.2 版本以后新增了这两个 API , 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。 简单来说,就是父组件通过 Provider 传入变量,任意子孙组件通过 Inject 来拿到变量。

  

 1 // 父组件
 2 <template>
 3   <div class="container">
 4     <button @click="this.changeName">我要改名字了</button>
 5     <p>我的名字:{{name}}</p>
 6     <child1></child1>
 7   </div>
 8 </template>
 9 
10 <script>
11 import Child1 from './component/child1.vue';
12 export default {
13   name: 'demo',
14   data: function() {
15     return {
16       name: '政采云'
17     };
18   },
19   // provide() {
20   //   return {
21   //     name: this.name //这种绑定方式是不可响应的
22   //   };
23   // },
24   provide() {
25     return {
26       obj: this
27     };
28   },
29   components: {
30     Child1
31   },
32   methods: {
33     changeName() {
34       this.name = '政采云前端';
35     }
36   }
37 };
38 </script>
39  
40 
41 // 子组件
42 <template>
43   <div>
44     <div class="center">
45       <!-- <p>子组件名字:{{name}}</p> -->
46       <p>子组件名字:{{this.obj.name}}</p>
47     </div>
48     <child2></child2>
49   </div>
50 </template>
51 
52 <script>
53 import child2 from './child2.vue';
54 
55 export default {
56   name: 'demo1',
57   data() {
58     return {};
59   },
60   props: {},
61   // inject: ["name"],
62   inject: {
63     obj: {
64       default: () => {
65         return {};
66       }
67     }
68   },
69   components: {
70     child2
71   },
72 };
73 </script>

需要注意的是: Provide 和 Inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。

所以,如果采用的是我代码中注释的方式,父级的 name 如果改变了,子组件this.name 是不会改变的,仍然是 政采云 ,而当采用代码中传入一个监听对象,修改对象中属性值,是可以监听到修改的。

Provider / Inject 在项目中需要有较多公共传参时使用还是颇为方便的。

小总结:传输数据父级一次注入,子孙组件一起共享的方式。

六、 $parent / $children & $refs

 • $parent / $children: 指定已创建的实例之父实例,在两者之间建立父子关系。子实例可以用 this.$parent 访问父实例,子实例被推入父实例的 $children 数组中。 

 • $refs: 一个对象,持有注册过 ref 特性[3] 的所有 DOM 元素和组件实例。ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件。 

  

 1 // 父组件
 2 <template>
 3   <div class="container">
 4     <p>我的title:{{title}}</p>
 5     <p>我的name:{{name}}</p>
 6     <child1 ref="comp1"></child1>
 7     <child2 ref="comp2"></child2>
 8   </div>
 9 </template>
10 
11 <script>
12 import Child1 from './component/child1.vue';
13 import Child2 from './component/child2.vue';
14 export default {
15   name: 'demo',
16   data: function() {
17     return {
18       title: null,
19       name: null,
20       content: '就是我'
21     };
22   },
23   components: {
24     Child1,
25     Child2
26   },
27   mounted() {
28     const comp1 = this.$refs.comp1;
29     this.title = comp1.title;
30     comp1.sayHello();
31     this.name = this.$children[1].title;
32   },
33 };
34 </script>
35  
36 
37 // 子组件1-ref方式
38 <template>
39   <div>
40     <div class="center">我的父组件是谁:{{content}}</div>
41   </div>
42 </template>
43 
44 <script>
45 export default {
46   name: 'demo1',
47   data() {
48     return {
49       title: '我是子组件',
50       content: null
51     };
52   },
53   mounted() {
54     this.content = this.$parent.content;
55   },
56   methods: {
57     sayHello() {
58       window.alert('Hello');
59     }
60   }
61 };
62 </script>
63  
64 
65 // 子组件2-children方式
66 <template>
67   <div>
68     <div class="center"></div>
69   </div>
70 </template>
71 
72 <script>
73 export default{
74   name: 'demo1',
75   data() {
76     return {
77       title: '我是子组件2'
78     };
79   },
80 };
81 </script>

通过例子可以看到这两种方式都可以父子间通信,而缺点也很统一,就是都不能跨级以及兄弟间通信。

小总结:父子组件间共享数据以及方法的便捷实践之一。

总结 

组件间不同的使用场景可以分为 3 类,对应的通信方式如下: 

• 父子通信:Props / $emit,$emit / $on,Vuex,$attrs / $listeners,provide/inject,$parent / $children&$refs 

• 兄弟通信:$emit / $on,Vuex 

• 隔代(跨级)通信:$emit / $on,Vuex,provide / inject,$attrs / $listeners

转载于:https://www.cnblogs.com/ommph/p/11466914.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值