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

pgccezyw  于 2023-01-05  发布在  Vue.js
关注(0)|答案(5)|浏览(277)

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

aiazj4mn

aiazj4mn1#

可以使用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();

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

        return { expandAll}
    }
}
</script>
s4chpxco

s4chpxco2#

您不会从子组件侦听父事件,而是将一个prop向下传递给子组件,如果子组件需要更新数据,您将从子组件向父组件发出一个事件以更新状态。
只读生命周期:上级〉 prop 〉下级
读取/更新生命周期:父项〉属性〉子项〉发射〉父项〉更新〉子项更新

klsxnrf1

klsxnrf13#

我提出了一种从父组件向子组件发送事件的方法。请注意,这是一种非常丑陋的变通方法!!

//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');
  })
}
rjzwgtxy

rjzwgtxy4#

如果你像我一样在Vue 2中的this.$root.$on(...)this.$root.$emit(...)上调用一些事件,从任何父/子到任何子/父,以某种方式保持你的代码更干净,而不是分别使用一堆发射和 prop ,让你的代码爆炸。
从Vue 3 doc,事件总线模式可以通过使用实现事件发射器接口的外部库来替换。使用实现pub-sub模式的库或编写它。Vue 3事件描述
现在,如果您使用Option-API(如vue 2),您需要导入该事件文件,然后在任何组件中使用它。
如果你使用的是<script setup>,你需要添加额外的步骤,以便你的事件库这里的代码。
下面是pub-sub javascript模式的一个基本示例,不要忘记添加off方法并在beforeUnmounted(v3)和beforeDestroy(v2)上调用它,以便每个挂载的调用不执行多个函数)

//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();

如果您正在执行vue 2,则语法为Option-API://in vue组件

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');
fivyi3re

fivyi3re5#

您可以通过使用作用域插槽来实现这一点。
例如,在"开槽"元件的父元件中:
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组件中,我们可以将一些方法触发事件的范围限定为父组件的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>

相关问题