zoukankan      html  css  js  c++  java
  • vue组件中的通信

    一、组件间的关系

    1、父子关系

    2、兄弟关系

    3、隔代关系

    二、组件间的通信方式

    1、props
    2、$emit/$on
    3、VUEX
    4、$parent/$children
    5、$attrs/$listeners
    6、provide/inject
     
    三、通信方式举例
     
    新建了一个过程,采用webpack来管理项目。

     方法一:props / $emit

    1、props---父组件向子组件传值
    子组件:sub1.vue
    父组件:app.vue
    父组件通过props向下传递给子组件。注:组件中的数据共有三种形式:data,props,computed
    // app.vue   父组件
    
    <template>
        <div>
            <sub1 v-bind:data_from_app = "msg_parent"></sub1>
        </div>
    </template>
    
    <script>
    import sub1 from './components/sub1.vue';
    
    export default {
        data(){
            return {
                msg_parent:'父组件数据app'
            }
        },
        components:{
            sub1
        }
    }
    </script>
    
    <style lang="scss" scoped>
    
    </style>
    

      

    // 子组件  sub1.vue  
    
    <template>
        <div>
            <p>{{data_from_app}}</p>
        </div>
    </template>
    
    <script>
    export default {
        data(){
            return {
                sub_msg:'子组件sub1数据',
            }
        },
        props:{
            data_from_app:{
                type:String,
                require:true
            }
        }
    }
    
    // 父组件通过props向下传递给子组件。注:组件中的数据共有三种形式:data,props,computed
    </script>
    
    <style lang="scss" scoped>
    
    </style>
    

      

    2、子组件向父组件传值---(通过事件的形式)--- $emit

    子组件通过events给父组件发送消息,实际就是把自己的数据发送到父组件  $emit发送  $event接收

    //  子组件 sub1.vue
    
    <template>
        <div>
            <p>{{data_from_app}}</p>
            <p @click="changeText">{{text_sub}}</p>
        </div>
    </template>
    
    <script>
    export default {
        data(){
            return {
                sub_msg:'子组件sub1数据',
                text_sub:'子组件原来的数据'
            }
        },
        props:{
            data_from_app:{
                type:String,
                require:true
            }
        },
        methods:{
            changeText(){
                // 自定义事件,传递值 "子组件向父组件传值" 
                this.$emit('titleChanged','子组件向父组件传值')
            }
        }
    }
    // 1、props
    // 父组件通过props向下传递给子组件。注:组件中的数据共有三种形式:data,props,computed
    </script>
    
    <style lang="scss" scoped>
    
    </style>
    

      

    // 父组件 app.vue
    
    <template>
        <div>
            <!-- 给子组件定义了一个点击事件changeText,
            在子组件中的时间定义中,使用$emit传递titleChanged和要传递给父组件参数,子组件用$emit来传递参数数据 -->
            <sub1 v-on:titleChanged="updateText" v-bind:data_from_app = "msg_parent"></sub1>
            <!-- 父组件通过$event来接收传递过来的参数 updateText($event)  -->
            <!-- 这里要与子组件 titleChanged  保持一致 -->
        </div>
    </template>
    
    <script>
    import sub1 from './components/sub1.vue';
    
    export default {
        data(){
            return {
                msg_parent:'父组件数据app',
                text:'父组件原本的text'
            }
        },
        methods:{
            updateText(e){
                this.text = e;
                console.log(this.text)
            }
        },
        components:{
            sub1
        }
    }
    </script>
    
    <style lang="scss" scoped>
    
    </style>
    

      

    方法二、$emit / $on

    这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量的实现了任何组件间的通信,包括父子、兄弟、隔代。当项目比较大时、可以考虑使用vuex。

    实现方式:

    var Event = new Vue()
    Event.$emit(事件名,数据) ;
    Event.$on(事件名,data => {});
    

      

    <div id="itany">
        <my-a></my-a>
        <my-b></my-b>
        <my-c></my-c>
    </div>
    <template id="a">
      <div>
        <h3>A组件:{{name}}</h3>
        <button @click="send">将数据发送给C组件</button>
      </div>
    </template>
    <template id="b">
      <div>
        <h3>B组件:{{age}}</h3>
        <button @click="send">将数组发送给C组件</button>
      </div>
    </template>
    <template id="c">
      <div>
        <h3>C组件:{{name}},{{age}}</h3>
      </div>
    </template>
    <script>
    var Event = new Vue();//定义一个空的Vue实例
    var A = {
        template: '#a',
        data() {
          return {
            name: 'tom'
          }
        },
        methods: {
          send() {
            Event.$emit('data-a', this.name);
          }
        }
    }
    var B = {
        template: '#b',
        data() {
          return {
            age: 20
          }
        },
        methods: {
          send() {
            Event.$emit('data-b', this.age);
          }
        }
    }
    var C = {
        template: '#c',
        data() {
          return {
            name: '',
            age: ""
          }
        },
        mounted() {//在模板编译完成后执行
         Event.$on('data-a',name => {
             this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
         })
         Event.$on('data-b',age => {
             this.age = age;
         })
        }
    }
    var vm = new Vue({
        el: '#itany',
        components: {
          'my-a': A,
          'my-b': B,
          'my-c': C
        }
    });    
    </script>
    

      

    $on 监听了自定义事件data-a 和 data-b,因为有时不确定何时会触发事件,一般会在mounted或created钩子中监听

    方法三、vuex

    vuex 的原理:

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

    各模块的介绍:

    • Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
    • dispatch:操作行为触发方法,是唯一能执行action的方法。
    • actions:操作行为处理模块,由组件中的$store.dispatch('action 名称', data1)来触发。然后由commit()来触发mutation的调用 , 间接更新 state。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
    • commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
    • mutations:状态改变操作方法,由actions中的commit('mutation 名称')来触发。是Vuex修改state的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
    • state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
    • getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。

    vuex是vue的状态管理器,存储的数据是响应式的,但是并不会保存起来,刷新之后就回到了初始状态。具体做法应该是在vuex里面数据改变时把数据拷贝一份保存到localStorage中,刷新之后,如果localstorage里面有保存的数据,取出来替换store里面的state

    方法四:$attrs / $listeners

    $attrs:包含了父级作用域中,不被prop所识别(且获取)的特性绑定(class和style除外),当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v-bind="$attrs"传入内部组件。通常配合interitAttrs选项一起使用。

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

    //  app.vue  最外层的组件,本身定义了foo boo  coo 三个数据
    // 此时里面的子组件是 sub1  ,给它使用了 foo boo coo  三个数据,但 sub1 本身只定义了 foo 这个属性
    
    <template>
        <div>
            <!-- 给子组件定义了一个点击事件changeText,
            在子组件中的时间定义中,使用$emit传递titleChanged和要传递给父组件参数,子组件用$emit来传递参数数据 -->
            <p>父组件中的数据:foo:{{foo}}、boo:{{boo}}、coo:{{coo}}</p>
            <sub1 :foo="foo"
                  :boo="boo"
                  :coo="coo"
                  v-on:titleChanged="updateText" 
                  v-bind:data_from_app = "msg_parent"></sub1>
            <!-- 父组件通过$event来接收传递过来的参数 updateText($event)  -->
            <!-- 这里要与子组件 titleChanged  保持一致 -->
    
            <!-- <subvue1></subvue1>
            <subvue2></subvue2> -->
    
    
        </div>
    </template>
    
    <script>
    import sub1 from './components/sub1.vue';
    // import subvue1 from './components/subVue/subvue1.vue';
    // import subvue2 from './components/subVue/subvue2.vue';
    
    export default {
        data(){
            return {
                msg_parent:'父组件数据app',
                text:'父组件原本的text',
                foo:'html',
                boo:'css',
                coo:'vue'
            }
        },
        methods:{
            updateText(e){
                this.text = e;
                console.log(this.text)
            }
        },
        components:{
            sub1,
            // subvue1,
            // subvue2
        }
    }
    </script>
    
    <style lang="scss" scoped>
    
    </style>
    

      

    // sub1  APP 的子组件,本身只定义了 foo 这个属性,因此通过$attrs拿到的是 boo  coo
    // 这里sub1有了一个子组件  subvue1,给这个子组件使用了$attrs,即boo  coo ,但本身子组件只有 boo 属性
    
    <template>
        <div>
            <p>{{data_from_app}}</p>
            <p @click="changeText">{{text_sub}}</p>
            <p>foo:{{foo}}</p>
            <p>sub1的$attrs:{{$attrs}}</p>
            <subvue1 v-bind="$attrs"></subvue1>
        </div>
    </template>
    
    <script>
    import subvue1 from './subVue/subvue1.vue'
    export default {
        data(){
            return {
                sub_msg:'子组件sub1数据',
                text_sub:'子组件原来的数据'
            }
        },
        inheritAttrs:false,  // 可已自动关闭挂载到根元素上没有在props声明的属性
        props:{
            data_from_app:{
                type:String,
                require:true
            },
            foo:String
        },
        created(){
            console.log(this.$attrs);
        },
        methods:{
            changeText(){
                // 自定义事件,传递值 "子组件向父组件传值" 
                this.$emit('titleChanged','子组件向父组件传值')
            }
        },
        components:{
            subvue1
        }
    }
    // 1、props
    // 父组件通过props向下传递给子组件。注:组件中的数据共有三种形式:data,props,computed
    </script>
    
    <style lang="scss" scoped>
    
    </style>
    

      

    //  subvue1   sub1 的子组件,本身只定义了 boo 这一个属性,因此 attrs  拿到的是 coo  
    
    <template>
        <div>
            <h3>subvue1组件</h3>
            <p>boo:{{boo}}</p>
            <!-- <button >subvue1组件:将数据发给组件subvue2</button> -->
            <p>subvue1的$attrs:{{$attrs}}</p>
        </div>
    </template>
    
    <script>
    import Vue from 'vue';
    
    export default {
        data(){
            return {
                data1:'subvue1',
                data_from:''
            }
        },
        props:{
            boo:String
        },
        mounted(){
    
        },
        created9(){
            console.log(this.$attrs);
        },
        methods:{
       
        }
    }
    </script>
    
    <style lang="sass" scoped>
    
    </style>
    

      

     小结:$attrs表示没有继承数据的对象,格式为{属性名:属性值},vue2.4提供了$attrs、$listeners来传递数据与实践,跨级组件之间的通信更加简单。

    $attrs 里面存放的是父组件中绑定的非props属性。$listeners 里面存放的是父组件中绑定的非原生事件。

    方法五:provide/inject

    二者需要一起使用。允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在上下游关系成立的时间里始终生效。就是:祖先组件中通过provide来提供变量,然后在子孙组件中通过inject来注入变量。

    主要解决了跨级组件中通信问题,不过他的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立一种主动提供与依赖注入的关系。

    // A.vue
    export default {
      provide: {
        name: '浪里行舟'
      }
    }
    

      

    // B.vue
    export default {
      inject: ['name'],
      mounted () {
        console.log(this.name);  // 浪里行舟
      }
    }
    

      

    provide/inject绑定并不是响应式的,这是刻意为之的。如果传入一个可监听对象,那么其对象的属性还是可响应的。所以,A.vue中name改变了,B.vue的this.name是不会改变的。

    方法六:$parent / $children 与 ref

    ref :如果在普通的dom元素上使用,引用指向的就是dom元素;如果用在子组件上,引用就指向组件实例。

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

    以上两种方法得到的都是组件实例,使用后可以调用组件的方法或访问数据。

    弊端:无法在跨级或兄弟间通信

    // component-a 子组件
    export default {
      data () {
        return {
          title: 'Vue.js'
        }
      },
      methods: {
        sayHello () {
          window.alert('Hello');
        }
      }
    }
    

      

    // 父组件
    <template>
      <component-a ref="comA"></component-a>
    </template>
    <script>
      export default {
        mounted () {
          const comA = this.$refs.comA;
          console.log(comA.title);  // Vue.js
          comA.sayHello();  // 弹窗
        }
      }
    </script>
    

      

    四、总结

    • 父子通信:

    父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners

    • 兄弟通信:

    Bus;Vuex

    • 跨级通信:

    Bus;Vuex;provide / inject API、$attrs/$listeners

  • 相关阅读:
    启动 Eclipse 弹出“Failed to load the JNI shared library jvm.dll”错误的解决方法!
    Eclipse 出现Some sites could not be found. See the error log for more detail.错误 解决方法
    Android sdk manager不能更新下载缓慢的解决方法
    Android图像处理之Bitmap类
    FAQ_1_陌生的VERSION.SDK_INT
    Android5.0新特性——新增的Widget(Widget)
    Android5.0新特性——兼容性(support)
    springmvc通过ajax异步请求返回json格式数据
    redhat7学习笔记之从零到部署javaweb项目
    ssm框架实现图片上传显示并保存地址到数据库
  • 原文地址:https://www.cnblogs.com/1220x/p/11741332.html
Copyright © 2011-2022 走看看