zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Windows下的符号链接
    简单谈谈存储器的容量缩水问题
    配置CKEditor和CKFinder
    CKFinder的水印控件的问题
    关于 Visual Studio 默认创建的不是公共类
    Symbian 60 按键以及对应的键值(图)
    PLC中存储器的数据类型与寻址方式
    S7200 寻址
    电工识图笔记
    S7200型号串口通信参数设置
  • 原文地址:https://www.cnblogs.com/ommph/p/11466914.html
Copyright © 2011-2022 走看看