Skip to content

事件

Livewire 提供了一个强大的事件系统,你可以用它在页面上的不同组件之间进行通信。由于它在底层使用浏览器事件,你还可以使用 Livewire 的事件系统与 Alpine 组件甚至纯 JavaScript 进行通信。

要触发事件,你可以在组件内的任何地方使用 dispatch() 方法,并从页面上的任何其他组件监听该事件。

派发事件

要从 Livewire 组件派发事件,可以调用 dispatch() 方法,将事件名称和你想要随事件一起发送的任何额外数据传递给它。

下面是从 post.create 组件派发 post-created 事件的示例:

php
<?php // resources/views/components/post/⚡create.blade.php

use Livewire\Component;

new class extends Component
{
    public function save()
    {
		// ...

		$this->dispatch('post-created'); // [tl! highlight]
    }
};

在这个示例中,当调用 dispatch() 方法时,将派发 post-created 事件,页面上监听此事件的每个其他组件都将收到通知。

你可以通过将数据作为第二个参数传递给 dispatch() 方法来与事件一起传递额外数据:

php
$this->dispatch('post-created', title: $post->title);

监听事件

要在 Livewire 组件中监听事件,在你希望在派发给定事件时调用的方法上方添加 #[On] 属性:

确保导入属性类

确保导入任何属性类。例如,下面的 #[On()] 属性需要以下导入 use Livewire\Attributes\On;

php
<?php // resources/views/components/⚡dashboard.blade.php

use Livewire\Component;
use Livewire\Attributes\On; // [tl! highlight]

new class extends Component
{
	#[On('post-created')] // [tl! highlight]
    public function updatePostList($title)
    {
		// ...
    }
};

现在,当从 post.create 派发 post-created 事件时,将触发网络请求并调用 updatePostList() 操作。

如你所见,随事件发送的额外数据将作为其第一个参数提供给操作。

监听动态事件名称

偶尔,你可能希望在运行时使用组件中的数据动态生成事件监听器名称。

例如,如果你想将事件监听器的范围限定为特定的 Eloquent 模型,可以在派发时将模型的 ID 附加到事件名称上,如下所示:

php
<?php // resources/views/components/post/⚡edit.blade.php

use Livewire\Component;

new class extends Component
{
    public function update()
    {
        // ...

        $this->dispatch("post-updated.{$post->id}"); // [tl! highlight]
    }
};

然后监听该特定模型:

php
<?php // resources/views/components/post/⚡show.blade.php

use Livewire\Attributes\On; // [tl! highlight]
use Livewire\Component;
use App\Models\Post;

new class extends Component
{
    public Post $post;

	#[On('post-updated.{post.id}')] // [tl! highlight]
    public function refreshPost()
    {
		// ...
    }
};

如果上述 $post 模型的 ID 为 3,则 refreshPost() 方法只会被名为 post-updated.3 的事件触发。

从特定子组件监听事件

Livewire 允许你在 Blade 模板中直接在单个子组件上监听事件,如下所示:

blade
<div>
    <livewire:edit-post @saved="$refresh">

    <!-- ... -->
</div>

在上述场景中,如果 edit-post 子组件派发 saved 事件,父组件的 $refresh 将被调用,父组件将被刷新。

除了传递 $refresh,你可以传递任何你通常会传递给 wire:click 等的方法。下面是调用 close() 方法的示例,它可能会做一些事情,比如关闭模态对话框:

blade
<livewire:edit-post @saved="close">

如果子组件随请求一起派发了参数,例如 $this->dispatch('saved', postId: 1),你可以使用以下语法将这些值转发给父方法:

blade
<livewire:edit-post @saved="close($event.detail.postId)">

使用 JavaScript 与事件交互

当你从应用程序内的 JavaScript 与 Livewire 的事件系统交互时,它变得更加强大。这解锁了应用程序中任何其他 JavaScript 与页面上的 Livewire 组件通信的能力。

在组件脚本内监听事件

你可以从 @script 指令中轻松监听组件模板内的 post-created 事件,如下所示:

html
@script
<script>
    $wire.on('post-created', () => {
        //
    });
</script>
@endscript

上面的代码片段将监听其注册所在组件的 post-created 事件。如果组件不再在页面上,则不会再触发事件监听器。

阅读更多关于在 Livewire 组件中使用 JavaScript 的信息 →

从组件脚本派发事件

此外,你可以从组件的 @script 内派发事件,如下所示:

html
@script
<script>
    $wire.dispatch('post-created');
</script>
@endscript

当运行上面的 @script 时,post-created 事件将被派发到它定义所在的组件。

要仅将事件派发到脚本所在的组件而不是页面上的其他组件(防止事件"冒泡"),可以使用 dispatchSelf():

js
$wire.dispatchSelf('post-created');

你可以通过将对象作为第二个参数传递给 dispatch() 来向事件传递任何额外参数:

html
@script
<script>
    $wire.dispatch('post-created', { refreshPosts: true });
</script>
@endscript

你现在可以从 Livewire 类和其他 JavaScript 事件监听器中访问这些事件参数。

下面是在 Livewire 类中接收 refreshPosts 参数的示例:

php
use Livewire\Attributes\On;

// ...

#[On('post-created')]
public function handleNewPost($refreshPosts = false)
{
    //
}

你还可以从事件的 detail 属性中的 JavaScript 事件监听器访问 refreshPosts 参数:

html
@script
<script>
    $wire.on('post-created', (event) => {
        let refreshPosts = event.detail.refreshPosts

        // ...
    });
</script>
@endscript

Read more about using JavaScript inside your Livewire components →

从全局 JavaScript 监听 Livewire 事件

或者,你可以从应用程序中的任何脚本使用 Livewire.on 全局监听 Livewire 事件:

html
<script>
    document.addEventListener('livewire:init', () => {
       Livewire.on('post-created', (event) => {
           //
       });
    });
</script>

上面的代码片段将监听从页面上任何组件派发的 post-created 事件。

如果你出于任何原因想要移除此事件监听器,可以使用返回的 cleanup 函数:

html
<script>
    document.addEventListener('livewire:init', () => {
        let cleanup = Livewire.on('post-created', (event) => {
            //
        });

        // 调用 "cleanup()" 将取消注册上面的事件监听器...
        cleanup();
    });
</script>

Alpine 中的事件

由于 Livewire 事件在底层是纯粹的浏览器事件,你可以使用 Alpine 监听它们甚至派发它们。

在 Alpine 中监听 Livewire 事件

例如,我们可以使用 Alpine 轻松监听 post-created 事件:

blade
<div x-on:post-created="..."></div>

上面的代码片段将监听来自分配了 x-on 指令的 HTML 元素的子级 Livewire 组件的 post-created 事件。

要监听来自页面上任何 Livewire 组件的事件,可以在监听器中添加 .window:

blade
<div x-on:post-created.window="..."></div>

如果你想访问随事件发送的额外数据,可以使用 $event.detail:

blade
<div x-on:post-created="notify('New post: ' + $event.detail.title)"></div>

Alpine 文档提供了有关监听事件的更多信息。

从 Alpine 派发 Livewire 事件

从 Alpine 派发的任何事件都可以被 Livewire 组件拦截。

例如,我们可以从 Alpine 轻松派发 post-created 事件:

blade
<button @click="$dispatch('post-created')">...</button>

与 Livewire 的 dispatch() 方法一样,你可以通过将数据作为第二个参数传递给方法来与事件一起传递额外数据:

blade
<button @click="$dispatch('post-created', { title: 'Post Title' })">...</button>

要了解更多关于使用 Alpine 派发事件的信息,请查阅 Alpine 文档

你可能不需要事件

如果你使用事件从子组件调用父组件的行为,你可以在 Blade 模板中使用 $parent 直接从子组件调用操作。例如:

blade
<button wire:click="$parent.showCreatePostForm()">Create Post</button>

了解更多关于 $parent 的信息

直接派发到另一个组件

如果你想使用事件在页面上的两个组件之间直接通信,可以使用 dispatch()->to() 修饰符。

下面是 post.create 组件直接将 post-created 事件派发到 dashboard 组件的示例,跳过监听该特定事件的任何其他组件:

php
<?php // resources/views/components/post/⚡create.blade.php

use Livewire\Component;

new class extends Component
{
    public function save()
    {
		// ...

		$this->dispatch('post-created')->to(component: Dashboard::class);
    }
};

将组件事件派发给自身

使用 dispatch()->self() 修饰符,你可以限制事件仅被触发它的组件拦截:

php
<?php // resources/views/components/post/⚡create.blade.php

use Livewire\Component;

new class extends Component
{
    public function save()
    {
		// ...

		$this->dispatch('post-created')->self();
    }
};

从 Blade 模板派发事件

你可以使用 $dispatch JavaScript 函数直接从 Blade 模板派发事件。当你想从用户交互(如按钮点击)触发事件时,这很有用:

blade
<button wire:click="$dispatch('show-post-modal', { id: {{ $post->id }} })">
    EditPost
</button>

在此示例中,当点击按钮时,将使用指定的数据派发 show-post-modal 事件。

如果你想直接将事件派发到另一个组件,可以使用 $dispatchTo() JavaScript 函数:

blade
<button wire:click="$dispatchTo('posts', 'show-post-modal', { id: {{ $post->id }} })">
    EditPost
</button>

在此示例中,当点击按钮时,show-post-modal 事件将直接派发到 Posts 组件。

测试派发的事件

要测试由组件派发的事件,在 Livewire 测试中使用 assertDispatched() 方法。此方法检查在组件的生命周期中是否派发了特定事件:

php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Livewire\CreatePost;
use Livewire\Livewire;

class CreatePostTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_dispatches_post_created_event()
    {
        Livewire::test(CreatePost::class)
            ->call('save')
            ->assertDispatched('post-created');
    }
}

在此示例中,测试确保当在 post.create 组件上调用 save() 方法时派发带有指定数据的 post-created 事件。

测试事件监听器

要测试事件监听器,你可以从测试环境派发事件,并断言响应事件执行了预期的操作:

php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Livewire\Dashboard;
use Livewire\Livewire;

class DashboardTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_updates_post_count_when_a_post_is_created()
    {
        Livewire::test(Dashboard::class)
            ->assertSee('Posts created: 0')
            ->dispatch('post-created')
            ->assertSee('Posts created: 1');
    }
}

在此示例中,测试派发 post-created 事件,然后检查 dashboard 组件是否正确处理事件并显示更新的计数。

使用 Laravel Echo 的实时事件

Livewire 与 Laravel Echo 配合得很好,可以使用 WebSockets 在网页上提供实时功能。

安装 Laravel Echo 是先决条件

此功能假设你已安装 Laravel Echo 并且 window.Echo 对象在你的应用程序中全局可用。有关安装 echo 的更多信息,请查阅 Laravel Echo 文档

监听 Echo 事件

想象你在 Laravel 应用程序中有一个名为 OrderShipped 的事件:

php
<?php

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderShipped implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public Order $order;

    public function broadcastOn()
    {
        return new Channel('orders');
    }
}

你可能从应用程序的另一部分派发此事件,如下所示:

php
use App\Events\OrderShipped;

OrderShipped::dispatch();

如果你只使用 Laravel Echo 在 JavaScript 中监听此事件,它看起来就像这样:

js
Echo.channel('orders')
    .listen('OrderShipped', e => {
        console.log(e.order)
    })

假设你已经安装并配置了 Laravel Echo,你可以从 Livewire 组件内监听此事件。

下面是一个 order-tracker 组件的示例,它监听 OrderShipped 事件以向用户显示新订单的视觉指示:

php
<?php // resources/views/components/⚡order-tracker.blade.php

use Livewire\Attributes\On; // [tl! highlight]
use Livewire\Component;

new class extends Component
{
    public $showNewOrderNotification = false;

    #[On('echo:orders,OrderShipped')]
    public function notifyNewOrder()
    {
        $this->showNewOrderNotification = true;
    }

    // ...
};

如果你有嵌入变量的 Echo 通道(如订单 ID),可以通过 getListeners() 方法而不是 #[On] 属性定义监听器:

php
<?php // resources/views/components/⚡order-tracker.blade.php

use Livewire\Attributes\On; // [tl! highlight]
use Livewire\Component;
use App\Models\Order;

new class extends Component
{
    public Order $order;

    public $showOrderShippedNotification = false;

    public function getListeners()
    {
        return [
            "echo:orders.{$this->order->id},OrderShipped" => 'notifyShipped',
        ];
    }

    public function notifyShipped()
    {
        $this->showOrderShippedNotification = true;
    }

    // ...
};

或者,如果你喜欢,可以使用动态事件名称语法:

php
#[On('echo:orders.{order.id},OrderShipped')]
public function notifyNewOrder()
{
    $this->showNewOrderNotification = true;
}

如果你需要访问事件载荷,可以通过传入的 $event 参数访问:

php
#[On('echo:orders.{order.id},OrderShipped')]
public function notifyNewOrder($event)
{
    $order = Order::find($event['orderId']);

    //
}

私有和存在通道

你还可以监听广播到私有和存在通道的事件:

INFO

在继续之前,请确保你已为广播通道定义了身份验证回调

php
<?php // resources/views/components/⚡order-tracker.blade.php

use Livewire\Component;

new class extends Component
{
    public $showNewOrderNotification = false;

    public function getListeners()
    {
        return [
            // 公共通道
            "echo:orders,OrderShipped" => 'notifyNewOrder',

            // 私有通道
            "echo-private:orders,OrderShipped" => 'notifyNewOrder',

            // 存在通道
            "echo-presence:orders,OrderShipped" => 'notifyNewOrder',
            "echo-presence:orders,here" => 'notifyNewOrder',
            "echo-presence:orders,joining" => 'notifyNewOrder',
            "echo-presence:orders,leaving" => 'notifyNewOrder',
        ];
    }

    public function notifyNewOrder()
    {
        $this->showNewOrderNotification = true;
    }
};