ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

vue组件通讯方式

2021-09-09 17:31:40  阅读:118  来源: 互联网

标签:vue name 通讯 props 组件 data 页面


          组件是 Vue.js 最强大的功能之一;组件可以扩展 HTML 元素,封装可重用的代码;而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。对于vue来说,组件之间的消息传递是非常重要的,下面是我对组件之间消息传递的常用方式的总结。

  • props 和 $emit  (常用)

  • $parent / $children 与 ref

  • provide 和 inject

  • $attrs 和 $listeners

  • $emit 和 $on  (非父子组件间通讯)

  • vuex状态管理

 

         1. props 和 $emit 

                父组件向子组件传递数据是通过props传递的,子组件传递数据给父组件是通过$emit触发事件,props 以单向数据流的形式可以很好的完成父子组件的通信。

               单向数据流:就是数据只能通过 props 由父组件流向子组件,而子组件并不能通过修改 props 传过来的数据修改父组件的相应状态。

               至于为什么这样做,Vue 官网做出了解释:  

                       

 1 // A页面 => 父组件页面
 2 <template>
 3   <div class='A'>
 4     A页面
 5     <B :message='message' @getChildData="getChildData"></B>
 6   </div>
 7 </template>
 8 <script>
 9 import B from './components/B.vue'
10 export default {
11   name: 'A',
12   data () {
13     return {
14       message: '来自A页面'
15     }
16   },
17  components: {
18     B
19   },
20   methods: {
21     //  执行子组件触发的事件
22     getChildData(val){
23         console.log(val)
24     }
25   }
26 }
27 </script>
28 
29 // B页面 => 子组件页面
30 <template>
31   <div class='A'>
32      B页面  
33      <button @click="handleClick(message)">{{message}}</button>
34   </div>
35 </template>
36 <script>
37 export default {
38   name: 'B',
39   data () {
40     return {
41 
42     }
43   },
44  props: {  
45 //   得到父组件传递过来的数据,这里的定义最好是写成数据校验的形式,免得得到的数据是我们意料之外的
46     message: {
47       type: String,
48       default: ''
49     }
50   },
51  methods: {
52     handleClick(val) { 
53          // 点击按钮传递数据给父组件
54          this.$emit("getChildData", val)
55     }
56   }
57 }
58 </script>

    1).父组件传递了message数据给子组件,并且通过v-on绑定了一个getChildData事件来监听子组件的触发事件;

  2).子组件通过props得到相关的message数据,最后通过this.$emit触发了getChildData事件 

 

         2. $parent / $children 与 ref

             ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据。

             $parent/$children:  访问父 / 子实例

             注意:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据,(弊端:无法跨级或兄弟间通信)

             官方提示:需要注意 $children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。

 1 // 父组件 => A页面
 2 <template>
 3   <div class='A'>
 4     A页面
 5     <B ref="comB"></B>
 6     <child />
 7   </div>
 8 </template>
 9 <script>
10 import B from './components/B.vue'
11 import child from './components/child.vue'
12 export default {
13   name: '',
14   data () {
15     return {
16        message: '来自A页面的message'
17     }
18   },
19   components: {
20     B,
21     child
22   },
23  mounted () {
24     //  通过ref给组件绑定名字comB,在父组件中,通过this.$refs.comB就可以访问了这个子组件了,包括访问子组件的data里面的数据,调用它的函数
25     console.log(this.$refs.comB.name) // 子组件页面
26     console.log(this.$refs.comB.getList()) // hello
27     // $children[1]获取的是第二个子组件,改变data里的childMessage值
28     const children = this.$children[1].childMessage = 'B页面的message'
29     console.log(children)  // B页面的message
30   }
31 }
32 </script>
33 
34 
35 // 子组件  => B页面
36 <template>
37   <div class='com'>
38     B页面
39     <C></C>
40   </div>
41 </template>
42 <script>
43 import C from './C.vue'
44 export default {
45   name: '',
46   data () {
47     return {
48       name: '子组件页面'
49     }
50   },
51   methods: {
52     getList () {
53       console.log('hello')
54       return 'hello'
55     }
56   }
57 }
58 </script>
59 
60 // 子组件 => child页面
61 <template>
62   <div class='child'>
63     {{childMessage}}
64   </div>
65 </template>
66 <script>
67 export default {
68   name: '',
69   data () {
70     return {
71       // 通过$parent获取父组件data中的message
72       childMessage: this.$parent.message
73     }
74   }
75 }
76 </script>

 

         3. provide 和 inject

             用于父组件向子孙组件传递数据,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效

             使用方法:provide在父组件中返回要传给下级的数据,inject在需要使用这个数据的子辈组件或者孙辈等下级组件中注入数据。

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

 1 // A页面
 2   <template>
 3     <div class='A'>
 4       A页面
 5       <B ref="comB"></B>
 6       <child />
 7     </div>
 8   </template>
 9   <script>
10  import B from './components/B.vue'
11  export default {
12    name: '',
13    data () {
14      return {
15         
16      }
17    },
18    provide: { 
19      // 设置provide:name,它的作用将name这个变量提供给它的所有子组件
20      name: '来自A页面的name'
21    }
22  }
23  </script>
24  
25  // B页面
26  <template>
27    <div class='com'>
28      B页面 {{nameB}}
29      <C></C>
30    </div>
31  </template>
32  import C from './C.vue'
33  export default {
34    name: '',
35    data () {
36      return {
37         nameB:this.name
38      }
39    },
40    inject: ['name'], // 通过inject注入从A组件中提供的name变量
41  }
42  </script>
43  
44  // C组件
45  <template>
46    <div class='dom'>
47      C页面 {{nameC}}
48    </div>
49  </template>
50  <script>
51  export default {
52    name: '',
53    data () {
54      return {
55         nameC:this.name
56      }
57    },
58    inject: ['name'],// C组件在B组件里,B组件在A组件里,此时通过inject可以获取到从A组件中提供的name变量
59  }
60  </script>

            上面的代码可以看到,在A.vue里,我们设置了一个provide:name,值为来自A页面的name,它的作用就是将name这个变量提供给它的所有子组件,在B.vue中通过inject注入从A组件中提供的name变量,B组件中就可以通过this.name访问这个变量了,C组件是B组件的子组件,B组件是A组件的子组件,因此C组件通过inject注入从A组件提供的name变量,也可以通过this.name访问,这就是 provide / inject API 最核心的用法。

 

         4. $attrs 和 $listeners

           1. 一般用props方法传递,如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想直接传递数据给组件C就没办法, 只能是组件A通过 props 将数据传给组件B,然后组件B获取到组件A 传递过来的数据后再通过 props 将数据传给组件C。虽然能够实现,但是代码并不美观。无关组件中的逻辑业务增多了,代码维护也变得困难,再加上如果嵌套的层级越多逻辑也复杂,

           2. 案例1可以使用vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用

           针对上面问题Vue 2.4 提供了$attrs 和 $listeners 来实现能够直接让组件A传递消息给组件C。

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

           我的理解是:接收除了props声明外的所有绑定属性(class、style除外),通过this.$attrs

           

 1   // 父组件 A页面
 2   <template>
 3    <div class='aom'>
 4      A页面
 5       <B :name='name'  :message='message' :content='content' 
 6     @handleClicks='handleClicks'></B>
 7     </div>
 8   </template>
 9   
10   <script>
11  import B from './components/B.vue'
12  export default {
13    name: '',
14    data () {
15      return {
16        name: '来自A页面的name',
17        message: 'hello',
18        content: 'content'
19      }
20    },
21        methods: {
22            handleClicks (e) {
23                console.log(e)   //  接收C组件传递过来的参数{content: 'content'}
24            }
25        }
26  }
27  </script>
28  
29  // 子组件 B页面
30  <template>
31    <div class='bom'>
32      B页面
33      <C  v-bind="$attrs"  v-on="$listeners"></C>
34    </div>
35  </template>
36  
37  <script>
38  import C from './C.vue'
39  export default {
40    name: '',
41    data () {
42      return {
43 
44      }
45    },
46    inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性,不管inheritAttrs为true或者false,子组件中都能通过$attrs属性获取到父组件中传递过来的属性。
47    props: {
48     name: String  // name作为props属性绑定
49    },
50    components: {
51      C
52    },
53    created () {
54      console.log(this.$attrs)  // {message: 'hello', content: 'content'}
55    },
56  }
57  </script>
58  
59  // 孙组件 C页面
60  <template>
61    <div class='com'>
62      C页面
63          <div @click="handleClick">点击</div>
64    </div>
65  </template>
66  <script>
67  
68  export default {
69    name: '',
70    data () {
71      return {
72  
73      }
74    },
75    props: {
76      message: String  // message作为props属性绑定
77    },
78    created () {
79      console.log(this.$attrs)  //  { content: 'content'}
80    },
81        methods: {
82            // 通过点击事件传递给父组件
83            handleClick () {
84            this.$emit('handleClicks', this.$attrs)
85        }
86      }
87  }
88  </script>

            由于子组件在props中声明了name属性,$attrs中只有message和content两个属性,输出结果为:{message: 'hello', content: 'content'},

            在子组件上通过v-bind=“$attrs”,可以将属性继续向下传递,让孙组件也能访问到父组件的属性(接收除了props声明外的所有绑定属性(class、style除外)),这样传递多个属性会显得更加便捷,如果想要添加其他属性,可以继续绑定属性。

             此时我们又想到了一个问题,C组件(孙组件)的信息,怎么同步给A组件呢? 

             vue2.4版本新增了$listeners 属性,我们在B组件上 绑定 v-on=”$listeners”, 在A组件中,监听C组件触发的事件。就能把C组件发出的数据,传递给A组件。

 

         5. $emit 和 $on  (非父子组件间通讯)

             两个组件不是父子关系时可以使用此方法,EventBus 通过新建一个 Vue 事件 bus 对象,然后通过 bus.$emit 触发事件,bus.$on 监听触发的事件。

             const EventBus = new Vue(); // 相当于又new了一个vue实例,Event中含有vue的全部方法;

             EventBus.$emit('msg',this.msg); // 发送数据,第一个参数是发送数据的名称,接收时还用这个名字接收,第二个参数是这个数据现在的位置;

             EventBus.$on('msg',function(msg){ 接收数据,第一个参数是数据的名字,与发送时的名字对应,第二个参数是一个方法,要对数据的操作  })

             

 1 // event-bus.js 页面
 2 import Vue from 'vue'
 3 export const EventBus = new Vue()  // 创建一个事件中间件并将其导出
 4 
 5 // A页面
 6 <template>
 7   <div class='aom'>
 8     A页面
 9     <B></B>
10     <C></C>
11   </div>
12 </template>
13 <script>
14 import B from './components/B.vue'
15 import C from './components/C.vue'
16 export default {
17   name: '',
18   data () {
19     return {}
20   },
21  components: {
22     B,
23     C,
24   }
25 }
26 </script>
27 
28 // B页面
29 <template>
30   <div class='bom'>
31     B页面
32     <div @click="handleClick"> 点击</div>
33     <C></C>
34   </div>
35 </template>
36 <script>
37 import { EventBus } from './event-bus.js'
38 import C from './C.vue'
39 export default {
40   name: '',
41   data () {
42     return {
43       num: 1
44     }
45   },
46   methods: {
47     handleClick () {
48       // 传递数据方通过 EventBus.$emit(方法名,传递的数据)触发兄弟组件的事件
49       EventBus.$emit('addition', {
50         num: this.num++
51       })
52     }
53   }
54 }
55 </script>
56 
57 // C页面
58 <template>
59   <div class='com'>
60     C页面
61     <div>接收{{count}}</div>
62   </div>
63 </template>
64 <script>
65 import { EventBus } from './event-bus.js'
66 export default {
67   name: '',
68   data () {
69     return {
70       count: 0
71     }
72   },
73   mounted () {
74     // 接收数据方通过mounted钩子触发EventBus.$on()
75     EventBus.$on('addition', val => {
76       this.count = this.count + Number(val.num)
77     })
78   }
79 }
80 </script>

            移除事件的监听:

                 import { eventBus } from 'event-bus.js'

                EventBus.$off('addition', {})

 

         6. vuex状态管理

             Vuex 是状态管理工具,实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。Action用于异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。

              1.   state:用于数据的存储,是store中的唯一数据源

              2.  getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算

              3.  mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件

              4.  actions:类似mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作

              5.  modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护详细的关于Vuex的介绍,可去产看官网文档 

 

 

               

 

标签:vue,name,通讯,props,组件,data,页面
来源: https://www.cnblogs.com/naycy/p/15245133.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有