【问题标题】:Vue 3: Emit event from parent to child componentVue 3:从父组件向子组件发出事件
【发布时间】:2021-07-26 00:31:01
【问题描述】:

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

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

【问题讨论】:

  • 可以不用道具吗?作为 prop 从父级传递给子级的反应性属性将自动允许您在子级上执行操作。
  • @AliGajani 我在 vue 3 中说过类似这样的操作 v3.vuejs.org/guide/migration/events-api.html
  • 通过 props 进行父子通信是一种策略,我认为 Vue 2 和 Vue 3 在这方面没有任何变化。如果你想从父子发送事件,props 是一种方式去。由于通过 parent 传递给 prop 的属性将是 data() 或 computed(),因此它将是响应式的,允许您在 child 上观察和执行操作。只是我的两分钱。
  • 你是如何在 vue 2 中使用这些方法将事件从父组件发送到子组件的?这些似乎是在全局可访问的“总线”上发出事件,因此它不是严格的父->子通信。您可以为此目的使用任何全局事件发射器吗?

标签: javascript vue.js vuejs2 vue-component vuejs3


【解决方案1】:

您可以使用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>

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      来自 Vue3 文档,事件总线模式可以通过使用实现事件发射器接口的外部库来替换。 使用实现发布-订阅模式的库或编写它。 vue3 event description

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

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

      这是一个 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();
      

      如果你正在做 vue2 之类的语法 Option-API: //在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"});
      

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

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

      //在&lt;script setup&gt;中使用它

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

      【讨论】:

        【解决方案4】:

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

        只读生命周期: 父>道具>子

        读取/更新生命周期: 父>道具>子>发射>父>更新>子更新

        【讨论】:

        • 例如“expandAll”怎么样?我有带有按钮的父级和带有可扩展行的数据表子级。每个子行都有一个孙数据表,其中包含可扩展的行。我想单击按钮并展开或折叠所有子数据表行和所有孙数据表行。如果我只是在道具中传递一个标志,它将保持行展开/折叠,并且不允许用户更改任何单行的状态...?
        猜你喜欢
        • 2021-02-12
        • 2018-09-30
        • 2021-04-16
        • 2021-01-14
        • 2020-10-06
        • 1970-01-01
        • 2018-09-30
        • 2019-08-10
        • 2017-07-12
        相关资源
        最近更新 更多