繁体   English   中英

Vue 3:从父组件向子组件发出事件

[英]Vue 3: Emit event from parent to child component

现在我需要从父组件向子组件发出事件。 我在 vue 版本 3 中看到$on$off$once实例方法被删除。 应用程序实例不再实现事件发射器接口。

现在如何在 vue 版本 3 中从父组件发出事件并从子组件监听?

您不会从子组件中侦听父事件,而是将道具向下传递给子组件,如果子组件需要更新数据,您将从子组件向父组件发出事件以更新 state。

只读生命周期:Parent > Prop > Child

读取/更新生命周期:Parent > Prop > Child > Emit > Parent > Update > Child Updates

您可以使用Refs访问子方法

https://v3.vuejs.org/guide/composition-api-template-refs.html

<!-- Parent -->
<template>
    <ChildComponent ref="childComponentRef" />
</template>

<script>
import { ref } from 'vue'
import ChildComponent from './components/ChildComponent.vue'

export default { 
  setup( ) {
    const childComponentRef = ref()
    childComponentRef.value.expandAll();
  }
}
</script>
<!-- Child -->
<script>
export default {
    setup() {
        const expandAll= () => {
            //logic...
        }

        return { expandAll}
    }
}
</script>

如果您像我一样在 Vue2 中的this.$root.$on(...)this.$root.$emit(...)上调用一些事件,从任何父/子到任何子/父,以某种方式保持您的代码清理器,而不是分别使用一堆发射和道具,让你的代码炸毁..

从 Vue3 文档,事件总线模式可以通过使用实现事件发射器接口的外部库来替换。 使用实现 pub-sub 模式的库或编写它。 vue3 事件描述

现在,如果您使用的是 Option-API(如 vue 2),您需要导入该事件文件,然后在任何组件中正确使用它。

如果您使用的是<script setup> ,则需要添加额外的步骤才能让事件库继承代码。

这是 pub-sub javascript 模式的基本示例,不要忘记添加 off 方法并在beforeUnmounted (v3)、 beforeDestroy (v2) 上调用它,以免每个挂载的调用都执行多个 function )

    //event.js
    
class Event{
        constructor(){
            this.events = {};
        }
    
        on(eventName, fn) {
            this.events[eventName] = this.events[eventName] || [];
            this.events[eventName].push(fn);
        }
        emit = (eventName, data)=> (this.events[eventName]) ? this.events[eventName].forEach( fn => fn(data)) : '' ;  
    }
    
    export default new Event();

如果你在做 vue2 之类的语法 Option-API: //in vue component

import event from './event';
//mounted or any methods
even.on('GG', data=> console.log(`GG Event received ${data}`))

//显然你必须从另一个组件发出它 //...

import event from './event';
//on mounted or methods click or...
even.emit('GG', {msg:"Vue3 is super Cool"});

如果您使用的是<script setup>这意味着默认情况下所有变量和方法都暴露给模板。

//in main.js
import event from './event.js';
//..
app.config.globalProperties.$event = event;
//..

//note if you have an error make sure that you split the the app chaining, like this :

let app = createApp(App);
app.config.globalProperties.$event = event;
app.mount('#app');

//添加一个名为useEvent.js的文件

// useEvent.js
import { getCurrentInstance } from 'vue'
export default  useEvent => getCurrentInstance().appContext.app.config.globalProperties.$event;

//在<script setup>中使用它

import useEvent from '@/useEvent'
const event    =  useEvent();
event.emit('GG');

我想出了一种将事件从父组件发送到子组件的方法。 请注意,因为这是一个非常丑陋的解决方法!

//child

setup(){
  // get the current instance
  const instance = getCurrentInstance();

  // function to find the parent instance
  const getParentComponent = (name: string) => {
    let component = null;
    if (instance) {
      let parent = instance.parent;
      while (parent && !component) {
        if (parent.type.name === name) {
          component = parent;
        }
        parent = parent.parent;
      }
      return component;
    } else {
      return null;
    }
  };

  // listener that will be called from within the parent component
  const eventFunction = () => {
    console.log('event fired !!')
  }
      
  onMounted(() => {
    const $parent = getParentComponent('ParentComponentName');

    // register the current instance to the parent component
    if($parent && $parent.props && $parent.props.childInstances){
      ($parent.props.childInstances as any[]).push(instance)
    }
  })

  return {
    eventFunction
  }
}
//parent

name: 'ParentComponentName',
props: {
  childInstances: {
    type: Array as PropType<any[]>,
    required: false, 
    default: () => [] as any[]
  }
},
setup(props){
  const emitChildComponentEvent = (event: string) => {
    if(props.childInstances.length > 0){
      props.childInstances.forEach((component: any) => {
        if(typeof component.proxy[event] === 'function'){
          component.proxy[event]()
        }
      })
    }
  }

  onMounted(() => {
    emitChildComponentEvent('eventFunction');
  })
}

您可以通过使用scoped slots来做到这一点。

例如,在“开槽”组件的父级中:

Parent.vue

<script setup lang="ts">
import { useToggle } from '@/composables'

const props = defineProps({
  show: {
    type: Boolean,
    required: true
  }
})

const { isOpen, close, open, toggle } = useToggle(props.show)
</script>

<template>
  <slot
    :on-open="open"
    :on-close="close"
    :on-toggle="toggle"
  />
</template>

*我们在其中使用了基本的切换可组合项。

Grandparent.vue组件中,我们可以再scope一些方法触发事件到parent的Child.vue组件,如下:

GrandParent.vue

<template>
  <Parent>
    <template #default="{ onToggle }">
      <Child 
        @toggle="onToggle" 
      />
    </template>
  </Parent>
</template>

*将#default更改为任何你的插槽名称,如果没有名称则保留为默认值。

然后在 Child.vue 组件中,我们确保向上发出事件:

Child.vue

<script setup lang="ts">
const emit = defineEmits<{
  (e: 'toggle'): void
}>()

const toggle = () => {
  emit('toggle')
}
</script>

<template>
  <button @click="toggle">Toggle Event</button>
</template>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM