在laravel应用程序中从父组件向子组件发出vue事件而不使用事件总线

Posted

技术标签:

【中文标题】在laravel应用程序中从父组件向子组件发出vue事件而不使用事件总线【英文标题】:Emitting a vue event from parent to child component in a laravel application without using event bus 【发布时间】:2021-04-02 14:48:21 【问题描述】:

我似乎记得能够通过在相应子组件的打开 <template> 标记中指定发出的事件来从父组件向子组件发出事件。但是由于某种原因,它不再在我的实现中工作。我是在妄想,还是这可能是旧版本 VueJS 中已弃用的功能,可能代替了事件总线架构?我目前正在使用Vue v2.6.12

在我的 $root vue 实例中:

var app = new Vue(
    el: '#app',
    router,
    data: () => (),  
    methods: 
        clearForm() 
            this.$emit('clear-form');
        
    
);

Laravel 布局:

<v-main>
    @yield('content')
</v-main>

Vue 路由器 /Laravel view/vue:

@extends('layouts.user')

@section('content')
    <transition name="slide">
        <router-view></router-view>
    </transition>
@endsection

在 $root 实例中调用 clearForm() 时,我不应该能够像这样在子组件中捕获发出的事件吗...

<template @clear-form="clearForm"></template>

<script>
    export default 
        data() 
            
        ,
        created() 
            this.$on('clearForm', clearForm);
        , 
        methods: 
            clearForm() 
                //this is the method i want to call when emitted from $root
            
        
    
</script>

更新:

我从@JoshuaAngnoe 中选择的答案是将发出的事件从$root Vue instance 传递给通过Vue-Router 服务的子组件的正确方法。但是在我的例子中,我还利用了Laravel 框架,这增加了额外的复杂性,因为 html 被分成两个文件(布局和视图)。

包含&lt;router-view&gt; 元素的Laravel 视图与实际上与$root Vue 实例位于同一层的Laravel 布局是分开的。所以需要一个额外的步骤才能让它工作......

已发出事件的声明,在所选答案中,位于 &lt;router-view&gt; 元素中:&lt;router-view @clear-form="clearForm" /&gt; 需要移出 &lt;router-view&gt; 元素,并移入子组件的开始模板标签中: &lt;template @clear-form="clearForm"&gt;.

我最初示例的结构是正确的。当我调用$on 来捕获子组件发出的事件时,我只是错过了$root

created() 
    this.$on('clearForm', clearForm);

需要:

created() 
    this.$root.$on('clearForm', clearForm);

【问题讨论】:

父母通过道具将东西传递给孩子(道具可以是可以运行的函数 - 基本上充当事件)。孩子向父母发出事件。至少在 2.x 中一直如此。但实际上,您应该解耦并使用状态。如果你觉得 Vuex 太过分了,请给Vue.observable() 一个旋转。使用状态,您可以将所有内容解耦,并且存储是您唯一的事实来源,在整个应用程序中同步。或者,我应该说:它被导入的任何地方。 我无法将道具直接传递给孩子。我在 laravel 布局中使用 vue-router,因此实际上并未指定子组件(即 vue-router 的当前主 vue 服务器)。我知道我以前遇到过这个问题,我只是忘记了在每个子组件/vue 中指定传递的道具/事件的位置。如果我没记错的话,它是在路由器服务的每个 vue 的开始模板标签中指定的。 您可以像这样将父函数传递给子&lt;ChildComponent :function="myFunction" /&gt;,然后父myFunction函数将绑定到子function这个函数 &lt;ChildComponent :method="parentMethod" /&gt; 像这样 问题是子组件实际上并没有被指定。它是通过 vue-router 动态注入的。所以永远不会有子组件声明,例如&lt;ChildComponent :method="parentMethod" /&gt; 【参考方案1】:

您需要在应用程序的生命周期挂钩中创建一个事件侦听器(Created 或 BeforeMount 或 Mounted)。

created() 
    this.$on('clear-form', function()
        console.log('Event from child component emitted')
    );
 

//或调用你的函数

created() 
    this.$on('clear-form', clearForm) //<-- this function must be declared in your methods or actions

【讨论】:

我更新了示例以反映您的建议,但它仍然无法正常工作。你能澄清一下缺少什么吗?【参考方案2】:

不使用事件总线是不可能的。 尽管还有另一种方法可以实现这一点,那就是使用props。 理论上,总结就是,将一个 prop 传递给子组件,并在子组件内的该 prop 上添加一个观察者。现在,从您当前发出事件的父级中,您只需更改 prop 值,在子组件中,观察者将被执行。

现在在实践中,

var app = new Vue(
    el: '#app',
    router,
    data: () => (
        clearForm: false,
    ),  
    methods: 
        clearForm() 
            this.clearForm = true;
        
    
);

现在将这个 clearForm 数据属性作为道具传递给你的子组件,并添加一个观察者来监听这个道具的变化

<template></template>

<script>
    export default 
        props: ['clearForm'],
        data() 
            
        ,
        created() 
          
        , 
        methods: 
            clearFormFunc() 
                //this is the method i want to call when emitted from $root
            
        ,
        watch: 
            clearForm(value)
                if (value) 
                    this.clearFormFunc();
                
            
        
    
</script>

更新 2

您可以通过在子组件的这些挂钩中的任何一个中添加以下代码行来在子组件createdmounted 挂钩中监听父母的事件。像之前触发它一样从父级触发事件,无需更改父级。

this.$events.listen('clearForm', this.clearFormFunc);

【讨论】:

子组件视图是通过 vue-router 注入的,因此无法传递传统的 prop,因为从未实际指定子组件的名称以便能够传递 prop。这就是为什么我试图将它绑定到子组件的打开 &lt;template&gt; 标记。 如果没有在父组件内部使用,怎么知道它是子组件? 在子组件 createdmounted 钩子中,您可以使用 this.$events.listen('clearForm', this.clearFormFunc); 监听从父组件触发的事件 我尝试在created()mounted() 中都使用$events.listen(),但它们都不起作用。 如果子组件没有明确说明,如何将 $root 实例中的 prop 传递给 vue-router 服务的子组件?【参考方案3】:

我是在妄想,还是这可能是旧版 VueJS 中已弃用的功能,

不,你没有妄想,如果我没记错的话,这在 Vue 1 中是可能的。

正如您所指出的,您正在使用 Vue Router,路由组件很可能会负责触发一个清除表单事件,并且您希望您的应用程序对此做出响应。这可以通过在&lt;router-view&gt;&lt;/router-view&gt; 上传递一些侦听器来实现,例如&lt;router-view @clear-form="clearForm()"&gt;&lt;/router-view&gt;

一个例子:

<div id="app">
  <h1>
  Hello
  </h1>
  <router-view @clear-form="clearForm"></router-view>
</div>
Vue.use(VueRouter)
new Vue(
    el: '#app',
  router: new VueRouter(
    routes: [
         path: '/', component:  template: `
        <div>
            Some page component
          <button @click="$emit('clear-form')">Clear form</button>
       </div>
      ` 
    ]
  ),
  
  methods: 
    clearForm() 
        alert("Clearing form")
    
  
);

现在子组件(路由组件)可以向 App 实例发送一个应该清除表单的信号。

jsfiddle 中的一个工作示例:https://jsfiddle.net/zkfu8x5s/3/

【讨论】:

我似乎读错了,你想要的方向是 Root -> Child 而不是 Child -> Parent... 无论如何,传递事件侦听器和参数(甚至像 Yasir 建议的回调函数也是可能的. 是的。我正在尝试将发出的事件从 $root (父级)传递给子组件(与 vue-router 一起提供)。小提琴是一个将事件从子级传递给父级的示例。 我已经更新了示例以相反的方式进行:jsfiddle.net/bc31duse/1 它基本上使用 $root 元素作为事件总线... 这仍然会从子级向父级/$root 发出一个事件。我需要从 $root/parent 向 child 发出一个事件。 是的,你是对的,我确实编写了基础设施,但按钮应该在父级中。我已经修复了小提琴以演示从父级发出的清晰表单将由子级处理:jsfiddle.net/92n41ar7/3

以上是关于在laravel应用程序中从父组件向子组件发出vue事件而不使用事件总线的主要内容,如果未能解决你的问题,请参考以下文章

在Vue.js中从父组件执行子方法

Angular中从父组件到子组件的数据共享

ReactJS中从父组件调用子函数时如何在子组件中设置状态

如何在Angular 5中从父组件继承子组件中的css样式

在反应中从父dom节点中删除子组件

如果数据属性在 VueJs 中从父组件传递到子组件,则无法将其作为对象进行操作