当前位置: 首页 > news >正文

Vue 系列之:组件通讯

子组件调用父组件方法

1、直接在子组件中通过 this.$parent.event 来调用父组件的方法

父组件:

<template>
  <p>
    <child></child>
  </p>
</template>
<script>
  import child from './child';
  export default {
    components: {
      child
    },
    methods: {
      fatherMethod() {
        console.log('测试');
      }
    }
  };
</script>

子组件:

<template>
  <p>
    <button @click="childMethod()">点击</button>
  </p>
</template>
<script>
  export default {
    methods: {
      childMethod() {
        this.$parent.fatherMethod();
      }
    }
  };
</script>

2、父组件使用 v-on 监听事件,子组件使用 $emit 件触发事件

@ 是 v-on 的缩写

父组件:

<template>
  <p>
    <child @method1="fatherMethod"></child>
  </p>
</template>
<script>
  import child from './child';
  export default {
    components: {
      child
    },
    methods: {
      fatherMethod(params) {
        console.log('测试', params);
      }
    }
  };
</script>

子组件:

<template>
  <p>
    <button @click="childMethod()">点击</button>
  </p>
</template>
<script>
  export default {
    methods: {
      childMethod() {
        this.$emit('method1', params); // params 为参数,可不传 
        // this.$emit('method1');
      }
    }
  };
</script>

3、父组使用 v-bind 绑定事件,子组件用 props 接收事件

: 是 v-bind 的缩写

父组件:

<template>
  <p>
    <child :method1="fatherMethod"></child>
  </p>
</template>
<script>
  import child from './child';
  export default {
    components: {
      child
    },
    methods: {
      fatherMethod() {
        console.log('测试');
      }
    }
  };
</script>

子组件:

<template>
  <p>
    <button @click="childMethod()">点击</button>
  </p>
</template>
<script>
  export default {
    props: {
      method1: {
        type: Function,
        default: null
      }
    },
    methods: {
      childMethod() {
        if (this.method1) {
          this.method1();
        }
      }
    }
  };
</script>

父组件调用子组件方法

1、通过 ref 直接调用子组件的方法

父组件:

<template>
    <div>
        <Button @click="fatherMethod">点击调用子组件方法</Button>
        <Child ref="child"/>
    </div>
</template>    

<script>
import Child from './child';
export default {
    methods: {
        fatherMethod() {
              this.$refs.child.childMethod();
        },
    },
}
</script>

子组件:

<template>
  <div>我是子组件</div>
</template>

<script>
export default {
  methods: {
    childMethod() {
      console.log('我是子组件的方法');
    },
  },
};

</script>

2、通过组件的$emit$on方法(可以,但是没必要)

父组件:

<template>
    <div>
        <Button @click="fatherMethod">点击调用子组件方法</Button>
        <Child ref="child"/>
    </div>
</template>    

<script>
import Child from './child';
export default {
    methods: {
        fatherMethod() {
           this.$refs.child.$emit("getChildMethod")    //子组件$on中的名字
        },
    },
}
</script>

子组件:

<template>
    <div>我是子组件</div>
</template>

<script>
export default {
    mounted() {
        this.$nextTick(function() {
            this.$on('getChildMethod', this.childMethod);
        });
     },
     methods: {
         childMethod() {
             console.log('我是子组件方法');
         }
     }
};
</script>

兄弟组件

  • 方法1:通过父组件作为中转

    • 通过 ref 和 $parent

    • 通过 provide 和 inject

  • 方法2:使用 EventBus 事件总线

  • 方法3:vuex,下一篇内容会讲

EventBus 使用方式

1、初始化——全局定义

可以将 eventBus 绑定到 vue 实例的原型上,也可以直接绑定到 window 对象上

//main.js

//注册方式一
Vue.prototype.$EventBus = new Vue();

//注册方式二
window.EventBus = new Vue();

2、监听事件

//使用方式一
this.$EventBus.$on('eventName', (param1, param2, ...) => {
    //需要执行的代码
})

//使用方式二
EventBus.$on('eventName', (param1, param2, ...) => {
    //需要执行的代码
})

3、触发事件

//使用方式一
this.$EventBus.$emit('eventName', param1, param2,...)

//使用方式二
EventBus.$emit('eventName', param1, param2,...)

4、移除监听事件

为了避免在监听时,事件被反复触发,通常需要在页面销毁时移除事件监听。或者在开发过程中,由于热更新,事件可能会被多次绑定监听,这时也需要移除事件监听。

//使用方式一
this.$EventBus.$off('eventName');

//使用方式二
EventBus.$off('eventName');

//移除所有
EventBus.$off();

5、示例

简单示例一:

<!--组件 A.vue-->
<script>
export default {  
  mounted() {  
    // 监听事件
    this.$EventBus.$on('custom-event', this.handleEvent)  
  },  
  methods: {  
    handleEvent(data) {  
      console.log(data)  
    }  
  }  
}
</script>

<!--组件 B.vue-->
<template>  
  <button @click="handleClick">触发事件</button>  
</template>  

<script>
export default {
  data() {
    return {
        str: '我来自 B 组件'
    }
  }
  methods: {
    handleClick() {
      // 触发事件
      this.$EventBus.$emit('custom-event', this.str)  
    }  
  }  
}  
</script>

示例二:

假设兄弟组件有三个,分别是 A、B、C 组件,A 组件如何获取 B 或者 C 组件的数据

这时候就可以使用 EventBus。EventBus 是一种发布/订阅模式,用于在组件之间传递事件和数据。A 组件可以监听由 B 或 C 组件发布的事件,并在事件处理函数中获取传递的数据。

思路:
A 组件中使用 Event.$on 监听事件
B、C 组件中使用 Event.$emit 触发事件

// A.vue  
<template>  
  <div>A 接收到的数据: {{ receivedData }}</div>  
</template>  
  
<script>  

export default {  
  data() {  
    return {  
      receivedData: null  
    };  
  },  
  mounted() {  
    // 监听事件  
    EventBus.$on('custom-event', (data) => {  
      this.receivedData = data.message;  
    });  
  },  
  beforeDestroy() {  
    // 组件销毁前,移除事件监听器  
    EventBus.$off('custom-event');  
  }  
};  
</script>
// B.vue 和 C.vue
<template>  
  <button @click="sendData">发送数据</button>  
</template>  

<script>  
export default {  
  methods: {
    sendData() {  
      const data = { message: 'I am from B' };
      // 触发事件
      EventBus.$emit('data-from-a', data);  
    }  
  }  
};  
</script>

多层组件(爷孙)

provide() 和 inject[]

用于将数据或方法暴露给组件树中的任何后代组件,哪怕是深层次的后代组件都可以访问到这些数据,而无需通过 props 层层传递。

注意:provide 和 inject 主要用于单向数据传递,即从祖先组件流向后代组件。虽然可以在后代组件中修改注入的数据,但这种做法会破坏单向数据流的原则,导致数据流向不清晰,难以调试,因此不建议这样做。

Vue2 用法:

<!--爷/父 组件-->
<template>
  <div id="app">
    <Children></Children>
  </div>
</template>

<script>
import Children from "./Children.vue";
export default {
  name: 'parent',
  components: { Children },
  provide() {
    return {
      parentEvent: this.myEvent,
      parentData: this.message,
      parentStr: '字符串数据'
    };
  },
  data() {
    return {
      message: 'data中的数据'
    }
  },
  methods: {
    myEvent(params1, params2) {
      console.log(params1, params2)
    },
  }
};
</script>
<!--子/孙 组件-->
<template>
    <el-button @click="handleClick">测试</el-button>
</template>

<script>
export default {
    name: 'child',
    inject: ["parentEvent", "parentData", "parentStr"],
    methods: {
        handleClick() {
            this.parentEvent('参数1', '参数2');
            console.log(this.parentData)
            console.log(this.parentStr)
        }
    }
};
</script>

从上到下依次打印:

参数1 参数2
data中的数据
字符串数据

Vue3 用法:

<!--爷/父 组件-->
<template>
  <div id="app">
    <Children></Children>
  </div>
</template>

<script setup>
import { ref, provide } from "vue";
import Children from "./Children.vue";
const message = ref('data中的数据');
const str = '字符串数据'
function myEvent(params1, params2) {
  console.log(params1, params2)
}
provide('parentEvent', myEvent);
provide('parentData', message);
provide('parentStr', str);
</script>
<!--子/孙 组件-->
<template>
    <el-button @click="handleClick">测试</el-button>
</template>

<script setup>
import { inject } from "vue";
const parentEvent = inject('parentEvent');
const parentData = inject('parentData');
const parentStr = inject('parentStr', '默认值');

function handleClick() {
    parentEvent('参数1', '参数2')
    console.log(parentData.value)
    console.log(parentStr)
}
</script>

细心的朋友已经发现:在 Vue3 中,子组件打印的是 parentData.value,这说明 parentData 是一个响应式对象。

直接总结:

特性Vue2Vue3
响应式支持provide 提供的数据不是响应式provide 提供的数据是响应式
默认值支持不支持默认值支持默认值,inject 的第二个参数就是默认值

$attrs 和 $listeners

$attrs

$attrs 是一个对象,包含了父组件传递给子组件的所有非 prop 属性(即没有在 props 中定义的属性)。

当你希望将父组件传递的属性传递给子组件的子组件时,可以使用 $attrs。

父组件:

<!-- 父组件 -->
<template>
  <div id="app">
    <Children :params1="params1" :params2="params2" />
  </div>
</template>

<script>
import Children from "./Children.vue";
export default {
  name: 'parent',
  components: { Children },
  data() {
    return {
      params1: '测试1',
      params2: '测试2',
      params3: '测试3',
    }
  },
  mounted() {
    setTimeout(() => {
      this.params2 += 'timeout'
    }, 5000);
  },
};
</script>

子组件:

<!-- 子组件 -->
<template>
    <div>
        <p>params1: {{ $attrs.params1 }}</p>
        <p>params2: {{ $attrs.params2 }}</p>
        <p>params3: {{ $attrs.params3 }}</p>
        <Groundson v-bind="$attrs" :params4="params4"/>
    </div>
</template>

<script>
import Groundson from "./Groundson.vue";
export default {
    name: 'children',
    components: { Groundson },
    props: {
        params1: {
            type: String,
            default: ""
        }
    },
    data() {
        return {
            params4: '测试4'
        }
    },
    mounted() {
        console.log("children $attrs:", this.$attrs);
    },
};
</script>

孙组件:

<!-- 孙组件 -->
<template>
    <div>
    </div>
</template>
<script>

export default {
    name: 'groundson',
    mounted() {
        console.log("groundson $attrs:", this.$attrs);
    },
};
</script>

页面:

在这里插入图片描述

打印:

在这里插入图片描述
总结:

  • 没有通过 v-bind 传递给子组件的,子组件的 $attrs 中不会有该属性

  • 通过 v-bind 传递给了子组件,但是子组件使用了 props 接收的,子组件的 $attrs 中不会有该属性

  • $attrs 中的属性值是响应式的

  • 在子组件中使用 v-bind=“$attrs” 可以将子组件的 $attrs 中的所有属性都传递给孙子组件,孙子组件也是按同样的规则接收

inheritAttrs 的作用:

观察页面元素发现:

在这里插入图片描述
子组件的根元素和孙子组件的根元素都多了一些属性

官方解释:默认情况下,父组件传递的,但没有被子组件解析为 props 的 attributes 绑定会被“透传”。这意味着当我们有一个单根节点的子组件时,这些绑定会被作为一个常规的 HTML attribute 应用在子组件的根节点元素上。我们可以通过设置 inheritAttrs 为 false 来禁用这个默认行为。

例如在子组件中加上 inheritAttrs: false

在这里插入图片描述
子组件根节点的属性消失了,由于没有在孙子组件中设置,孙子组件的根节点还保留着属性

$listeners

$listeners 包含了父组件传递给子组件的所有事件监听器(即 v-on 绑定的事件)。

与 $attrs 类似, $attrs 是传递属性, $listeners 是传递方法。这里就不再举例了。

注意:在 Vue3 中,$listeners 已经被移除,其功能被合并到了 $attrs 中。

相关文章:

  • Docker基础篇——什么是Docker与Docker的仓库、镜像、容器三大概念
  • CS144 Lab Checkpoint 2: the TCP receiver
  • 【哇! C++】类和对象(三) - 构造函数和析构函数
  • B站文生视频模型工程实践
  • 使用CSS Grid布局时,如何避免元素重叠?
  • 鸿蒙开发学习笔记
  • mysql foreign_key_checks
  • 前端跨域设置 withCredentials: true
  • Redis 基本数据类型总结及使用场景
  • 面试题02.02.返回倒数第k个节点
  • ALG(Alloy+Loki+Grafana)轻量级日志系统
  • var vs let 与 const
  • 求职面试常见问题:数组与链表
  • 2025DNS二级域名分发PHP网站源码
  • 阿里云操作系统控制台——ECS操作与性能优化
  • doris: PostgreSQL
  • c语言笔记 函数指针
  • K8S学习之基础九:k8s中pod的生命周期
  • Spark(5)host配置
  • 线上图片验证码性能优化base64编码
  • 网站免费正能量软件/seo实战培训教程
  • 做系统的图标下载网站/页面优化算法
  • 湖南企业网站制作/上海搜索推广
  • 做期货关注网站/湖北seo整站优化
  • 加快建设服务型政府网站/公司做网站一般多少钱
  • wordpress建站 百度网盘/百度快照关键词推广