Vue组件间相互传值,组件通信,

                1. 父 => 子通信:pros;

                2. 子 => 父通信:  $emit;   $parent / $children;   $ref 也可以;

                3. 兄弟通信:$Bus;   Vuex;

                4. 跨级通信: $Bus;  Vuex;  provide / inject ;  $attrs / $listeners

1.父组件 传值给 子组件

        1.1.props

        父组件通过属性的方式向子组件传值,子组件通过 props 来接收。

// 父组件
<user-detail :name="name" />  <!--通过属性  :myName="name"  传值-->
    
export default {
    components: {
        UserDetail
    }
    ......
}

在子组件中使用props(可以是数组也可以是对象)接收即可。可以传多个属性。

// 子组件
export default {
    props: ['name']
}
​
/*
props: { name: String } //这样指定传入的类型,如果类型不对会警告
props: { name: [String, Number] } // 多个可能的类型
prosp: { name: { type: String, requires: true } } //必填的的字符串
props: { 
    name: { 
        type: Array, 
        default: () => []    // default指定默认值
    }
} 
*/

2.子组件 传值给 父组件

        2.1.自定义事件  $emit() 

在父组件中绑定一个事件(自定义事件),通过 this.$emit() 在子组件中触发

通过函数返回数值

// 子组件
<button @click="testName">改变父组件的name</button>
​
export default {
    methods: {
        //子组件的事件
        testName() {
            this.$emit('Test', this.name) // 通过this.$emit触发父组件中test事件,并传参this.name  (可以携带多个值,和多种数据类型)
            // 注:此处事件名称与父组件中绑定的事件名称要一致
        }
    }
}
// 父组件
<child @Test="Test"></child>//绑定自定义事件 test
​
methods: {
    Test(name) {  // name形参是子组件中传入的值Jack
        this.name = name
    }
}

        2.2. 通过 callback 函数

// 父组件
<child :callback="callback"></child>
​
methods: {
    callback: function(name) {
        this.name = name
    }
}
// 子组件
<button @click="callback('Jack')">改变父组件的name</button>
​
props: {
    callback: Function,
}

        2.3. $attrs 和 $listeners

        多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,这就有点大材小用了。所以就有了 $attrs / $listeners ,通常配合 inheritAttrs 一起使用。

$attrs:包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件。当一个组件没有声明任何 props 时,它包含所有父作用域的绑定 (class 和 style 除外)。

$listeners:包含了父作用域中的 (不含 .native 修饰符) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件。它是一个对象,里面包含了作用在这个组件上的所有事件监听器,相当于子组件继承了父组件的事件。

//father.vue 组件:
<template>
   <child :name="name" :age="age" :infoObj="infoObj" @updateInfo="updateInfo" @delInfo="delInfo" />
</template>
<script>
    import Child from '../components/child.vue'

    export default {
        name: 'father',
        components: { Child },
        data () {
            return {
                name: 'Lily',
                age: 22,
                infoObj: {
                    from: '上海',
                    job: 'policeman',
                    hobby: ['reading', 'writing', 'skating']
                }
            }
        },
        methods: {
            updateInfo() {
                console.log('update info');
            },
            delInfo() {
                console.log('delete info');
            }
        }
    }
</script>
//father.vue 组件:
<template>
   <child :name="name" :age="age" :infoObj="infoObj" @updateInfo="updateInfo" @delInfo="delInfo" />
</template>
<script>
    import Child from '../components/child.vue'

    export default {
        name: 'father',
        components: { Child },
        data () {
            return {
                name: 'Lily',
                age: 22,
                infoObj: {
                    from: '上海',
                    job: 'policeman',
                    hobby: ['reading', 'writing', 'skating']
                }
            }
        },
        methods: {
            updateInfo() {
                console.log('update info');
            },
            delInfo() {
                console.log('delete info');
            }
        }
    }
</script>
child.vue 组件:
<template>
    <grand-son :height="height" :weight="weight" @addInfo="addInfo" v-bind="$attrs" v-on="$listeners"  />
    // 通过 $listeners 将父作用域中的事件,传入 grandSon 组件,使其可以获取到 father 中的事件
</template>
<script>
    import GrandSon from '../components/grandSon.vue'
    export default {
        name: 'child',
        components: { GrandSon },
        props: ['name'],
        data() {
          return {
              height: '180cm',
              weight: '70kg'
          };
        },
        created() {
            console.log(this.$attrs); 
       // 结果:age, infoObj, 因为父组件共传来name, age, infoObj三个值,由于name被 props接收了,所以只有age, infoObj属性
            console.log(this.$listeners); // updateInfo: f, delInfo: f
        },
        methods: {
            addInfo () {
                console.log('add info')
            }
        }
    }
</script>
//grandSon.vue 组件:
<template>
    <div>
        {{ $attrs }} --- {{ $listeners }}
    <div>
</template>
<script>
    export default {
        ... ... 
        props: ['weight'],
        created() {
            console.log(this.$attrs); // age, infoObj, height 
            console.log(this.$listeners) // updateInfo: f, delInfo: f, addInfo: f
            this.$emit('updateInfo') // 可以触发 father 组件中的updateInfo函数
        }
    }
</script>

3.兄弟组件间传值

        3.1.全局事件总线

如果要了解其原理,需要先了解JavaScript原型链

main.js 中全局安装事件总线

因为只有组件对象或者Vue的实例化对象才能调用$on和$emit

想要成为事件总线的条件:
(1) 所有的组件对象必须都能看得到这个总线对象,因此我们把这个对象放在了Vue原型
(2) 这个事件总线对象必须能调用$on和$emit方法(总线对象必须是Vue的实例化对象或者是组件对象)
//创建实例
new Vue({
    el: '#app',
    render: h => h(App),
    beforeCreate() {
        Vue.prototype.$bus = this //安装事件总线
    }
})

A组件 ==传值==> B组件 为例

//A 组件,触发
this.$bus.$emit('getName',this.xxx) 
// B 组件,绑定
mounted(){
    this.$bus.$on('getName',this.name)
},

        3.2.通过 $emit (自定义事件) 和 props 结合的方式

        也就是,找一个父组件或子组件,作为桥梁(或者叫跳板),实现兄弟组件间通讯;但使用次数多的情况下,不推荐使用;

        具体方法,请看本文  1.1  和 2.1 章节。

        3.3.Vuex

        了解 Vuex 详细的使用方法,请点击 [ / 我 ]。

4. 多层父子组件通信

        4.1.provide/inject

        祖父和孙子通信,或者是跨越了更多层级的父子组件,这种情况不可能由子组件一级一级的向上传递参数,特别是在组件层级比较深,嵌套比较多的情况下,会导致代码很混乱。

provide/inject:

        父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量,不管组件层级有多深,在父组件生效的生命周期内,这个变量就一直有效。

//父组件
export default {
  provide: { // 它的作用就是将 **name** 这个变量提供给它的所有子组件。
    name: this.name
  }
}
//子组件
export default {
  inject: ['name'], // 注入了从父组件中提供的name变量
  mounted () {
    console.log(this.name); 
  }
}

注意:provide & inject 的绑定不是响应式。父组件的name发生变化,子组件不会随之变化。

实现 provide 和 inject 数据响应

         provide 祖先组件的实例,然后在子孙组件中注入依赖,这样就可以在后代组件中直接修改祖先组件的实例的属性,不过这种方法有个缺点就是这个实例上挂载很多没有必要的东西,如:props,methods;

// 父组件 
<div>
      <button @click="changeName">修改姓名</button>
</div>
<script>
    ......
    data() {
        return {
            name: "奥利给"
        };
    },
    provide() {
        return {
            parentObj: this //提供祖先组件的实例
        };
    },
    methods: {
        changeName() {
            this.name = '哈拉少'
        }
    }
</script>
//后代组件
<template>
  <div class="border2">
    <P>姓名:{{parentObj.name}}</P>
  </div>
</template>
<script>
  export default {
    inject: {
      parentObj: {
        default: () => ({})
      }
    } // 或者inject: ['parentObj']
  };
</script>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值