父子组件、兄弟组件、祖孙组件通信

632 阅读1分钟

父子组件通信

1.父组件向子组件传值

父组件通过使用子组件标签,在内部配置v:bind指令传属性,也就是:title=“title” :obj="obj"等等,而子组件通过配置props:{}来接收父组件传递过来的属性

**父组件**
<template>
   <div class="father">
      <son :title="title" :obj="obj"></son>
   </div>
</template>
<script>
import son from './component/son';
export default {
    name: 'index',
    components: {
        son,
    },
    data () {
        return {
            title: '父组件的title',
            obj: {
                value: 'value',
            },
        };
    },
};
</script>
**子组件**
<template>
   <div class="son">
      <div>title:{{title}}</div>
      <div>obj的value:{{obj.value}}</div>
   </div>
</template>

<script>
export default {
    name: 'son',
    props: {
        title: {
            type: String,
            default: '',
        },
        obj: {
            type: Object,
            default: () => ( {} ),
        },
    },
};
</script>
2.子组件修改父组件属性,调用父组件方法
  • 方法一:子组件配置 $emit传值, 父组件配置 v-on 接收
**父组件**
<template>
   <div class="father">
      <son
         :title="title"
         @changeTitle="changeTitle"
         @logFun="logFun"></son>
   </div>
</template>
<script>
import son from './component/son';
export default {
    name: 'index',
    components: {
        son,
    },
    data () {
        return {
            title: '',
        };
    },
    methods: {
        logFun () {
            console.log( '父组件的输出' );
        },
        changeTitle ( title ) {
            this.title = title;
        },
    },
};
</script>
 **子组件**
<template>
   <div class="son">
      <el-button type="primary" @click="changetitle">
         改变父组件title值
      </el-button><br>
      <el-button type="primary" @click="useFatherFun">
         调用父组件方法
      </el-button><br>
   </div>
</template>
<script>
export default {
    name: 'son',
    props: {
        title: {
            type: String,
            default: '',
        },
    },
    methods: {
        // 改变父组件title值
        changetitle () {
            this.title = this.title + '!';
            this.$emit( 'changeTitle', this.title );
        },
        // 调用父组件logFun方法
        useFatherFun () {
            this.$emit( 'logFun' );
        },
    },
};
</script>
  • 方法二:
    • 使用.sycn修饰符 通过“双向数据绑定”的形式更改props值,这种方式可以省去父组件用v-on接收
    • 通过包装对象的形式更改props传递的值(不推荐,避免这个参数多个子组件引用,无法找到造成数据不正常的原因;违反vue的prop单向下行绑定规范)
    • 在子组件中改变数组(使用改变原数组的数组方法对数组进行操作)(不推荐)
    • 使用this.parent调用父组件的方法(this.parent调用父组件的方法(this.parent可以获取父组件的属性、方法,如果多个父组件都拥有这个属性/方法,则都调用)
**父组件**
<template>
   <div class="father">
      <div>title:{{title}}</div>
      <div>obj:{{obj.value}}</div>
      <div>arr:{{arr}}</div>
      <div>text.value:{{text.value}}</div>
      <son
         :title.sync="title"
         :obj="obj"
         :arr="arr"
         :text.sync="text"></son>
   </div>
</template>
<script>
import son from './component/son';
export default {
    name: 'index',
    components: {
        son,
    },
    data () {
        return {
            title: '',
            obj: {
                value: 'value',
            },
            arr: [1, 2],
            text: {
                value: '请输入姓名',
            },
        };
    },
    methods: {
        logFun () {
            console.log( '父组件的输出' );
        },
    },
};
</script>
 **子组件**
<template>
   <div class="son">
      <!--与父组件的通信-->
      <el-button type="primary" @click="changetitle">
         通过“双向数据绑定”的形式更改props值
      </el-button><br>
      <el-button type="primary" @click="changeValue">
         通过包装对象的形式更改props传递的值
      </el-button><br>
      <el-button type="primary" @click="changeArr">
         在子组件中改变数组
      </el-button><br>
      <el-input type="primary" v-model="text.value"></el-input>
      <el-button type="primary" @click="useFatherFun">
         调用父组件方法
      </el-button>
   </div>
</template>
<script>
export default {
    name: 'son',
    components: {
        grandSon,
    },
    data () {
        return {
            age: 12,
            name: '李四',
        };
    },
    props: {
        title: {
            type: String,
            default: '',
        },
        obj: {
            type: Object,
            default: () => ( {} ),
        },
        arr: {
            type: Array,
            default: () => ( [] ),
        },
        text: {
            type: Object,
            default: () => ( {} ),
        },
    },
    methods: {
        // 改变父组件title值
        changetitle () {
            this.title = this.title + '!';
            this.$emit( 'update:title', this.title );
        },
        // 改变父组件obj.value值
        changeValue () {
            this.obj.value = 'value1';
        },
        // 改变父组件arr值(改变原数组的方法都可以)
        changeArr () {
            this.arr.push( 3 );
            // pop、shift、unshift、splice、sort都可
        },
        // 调用父组件的方法
        useFatherFun () {
            this.$parent.logFun();
        }
    },
};
</script>
3. 父组件调用子组件方法
  • 方法一:$refs
<son ref="sonCom"></son>
this.$refs.sonCom.sonFun(); // 给子组件绑定ref 通过this.$ref调用子组件的方法
  • 方法二:$children
    • 利用$children获取到的是一个数组,访问其中的组件必须通过索引值。
    • 由于可能会使用外部组件库如elementUI中的组件,也会被识别为子组件,有可能会造成混淆
    • 当子组件过多时,往往不能确定他的索引值
    • $children可以获取子组件的属性、方法
this.$children[0].sonFun(); // sonFun是子组件一的方法
this.$children[1].sonFun2(); // sonFun2是子组件二的方法

兄弟组件通信

  1. 两个子组件通过父组件通信
  2. vuex
  3. 全局事件总线
    在main.js中定义全局事件总线
new Vue( {
    el: '#app',
    render: h => h( App ),
    beforeCreate () {
        Vue.prototype.$eventBus = this; // beforeCreate钩子函数里this指代new出来的vm
    },
} );

Vue.prototype.$eventBus = new Vue()

 **组件一**
<template>
   <div class="son">
      <el-button type="primary" @click="changeName">
         给兄弟组件son1传name
      </el-button><br>
      <el-button type="primary" @click="transmitAge">
         给兄弟组件son1传age
      </el-button><br>
   </div>
</template>
<script>
export default {
    name: 'son',
    components: {
        grandSon,
    },
    data () {
        return {
            age: 12,
            name: '李四',
        };
    },
    methods: {
        // 给兄弟组件son1传name
        changeName () {
            this.$eventBus.$emit( 'getName', this.name );
        },
        // 给兄弟组件son1传age
        transmitAge () {
            this.$eventBus.$emit( 'getAge', this.age );
        },
    },
};
</script>
**组件二**
<template>
   <div class="son1">
      姓名:{{name}}
      年龄:{{age}}
   </div>
</template>
<script>
export default {
    name: 'son1',
    data () {
        return {
            name: '张三',
            age: 0,
        };
    },
    mounted () {
        // 获取兄弟组件son传来的name
        this.$eventBus.$on( 'getName', ( name ) => {
            this.name = name;
        } );
        // 获取兄弟组件son传来的age
        this.$eventBus.$on( 'getAge', ( age ) => {
            this.age = age;
        } );
    },
    // 销毁“全局事件总线”中定义的自定义事件
    // “全局事件总线”中定义的自定义函数是一直存在的,哪怕使用组件销毁了,但是Vue实力定义的“全局事件总线”中还是会存在自定义事件,所以需要在组件销毁之前进行解绑
    beforeDestroy () {
        this.$eventBus.$off( 'getName' );
        this.$eventBus.$off( 'getAge' );
    },
};
</script>

祖孙组件通信

  1. 全局事件总线$eventBus
  2. vuex
  3. 使用attr/attr / listener
    祖A 父B 孙C, B调用C时使用v-on绑定listeners ,通过v−bind绑定attrs,C组件可以直接获取到A组件传递下来的props,除了B中props接收过的 (attrs将B未接收的A组件的绑定传给C,listeners将B未接收的A的监听传给C)
**A**
<template>
   <div>
      <div>这是孙组件的grandSonInfo:{{grandSonInfo}}</div>
      <son
         :title="title"
         :grandSonText="grandSonText"
         :grandSonInfo.sync="grandSonInfo"
         @logFun="logFun"></son>
      <son1></son1>
   </div>
</template>

<script>
import son from './component/son';
import son1 from './component/son1';
export default {
    name: 'index',
    components: {
        son,
        son1,
    },
    data () {
        return {
            title:'父组件的title',
            grandSonText: 'grandSon信息',
            grandSonInfo: 'grandSonInfo',
        };
    },
    methods: {
        logFun () {
            console.log( '父组件的输出' );
        },
    },
};
</script>
**B**
<template>
   <div class="son">
       父组件的title:{{title}}
      <grand-son v-bind="$attrs" v-on="$listeners"></grand-son>
   </div>
</template>

<script>
import grandSon from './grandSon';
export default {
    name: 'son',
    components: {
        grandSon,
    },
    props: {
        title: {
            type: String,
            defult: '',
        },
    },
    data () {
        return {
            age: 12,
            name: '李四',
        };
    },
};
</script>
**C**
<template>
   <div>
      <div>grandSonText:{{grandSonText}}</div>
      <div>grandSonInfo:{{grandSonInfo}}</div>
      <el-button type="success" @click="changeGrandSonInfo">
         改变祖组件A的grandSonInfo
      </el-button>
      <el-button type="success" @click="useLogFun">
         调用祖组件A的logFun
      </el-button>
   </div>
</template>

<script>
export default {
    name: 'grandSon',
    props: {
        grandSonText: {
            type: String,
            default: '',
        },
        grandSonInfo: {
            type: String,
            default: '',
        },
        logFun: {
            type: Function,
            default: () => () => ( {} ),
        },
    },
    methods: {
        // 改变祖组件A的grandSonInfo的值
        changeGrandSonInfo () {
            this.$emit( 'update:grandSonInfo', this.grandSonInfo + '~' );
        },
        // 调用祖组件A的logFun方法
        useLogFun () {
            this.$emit( 'logFun' );
        },
    },
};
</script>