事件
Livewire 提供了强大的事件系统,可用于页面上不同组件之间的通信。由于底层使用的是浏览器事件,你也可以用 Livewire 的事件系统与 Alpine 组件甚至普通的原生 JavaScript 通信。
要触发事件,可在组件内任意位置使用 dispatch() 方法,并在页面上的其他组件中监听该事件。
派发事件
要从 Livewire 组件派发事件,可调用 dispatch() 方法,传入事件名以及希望随事件一并发送的附加数据。
下面是从 post.create 组件派发 post-created 事件的示例:
<?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() 方法的第二个参数随事件传递附加数据:
$this->dispatch('post-created', title: $post->title);监听事件
要在 Livewire 组件中监听事件,在希望在事件派发时被调用的方法上方添加 #[On] 属性:
WARNING
请确保导入 Attribute 类
请确保导入所用的 Attribute 类。例如,下面的 #[On()] 属性需要导入:use Livewire\Attributes\On;。
<?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() action。
如你所见,随事件发送的附加数据会作为 action 的第一个参数传入。
监听动态事件名
有时你可能希望在运行时使用组件中的数据动态生成事件监听器名称。
例如,若希望将事件监听器限定到特定的 Eloquent 模型,可以在派发时把模型的 ID 追加到事件名上,如下所示:
<?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 // 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,则只有名为 post-updated.3 的事件才会触发 refreshPost() 方法。
监听特定子组件的事件
Livewire 允许你在 Blade 模板中直接针对单个子组件监听事件,如下所示:
<div>
<livewire:edit-post @saved="$refresh">
<!-- ... -->
</div>在上述场景中,如果 edit-post 子组件派发了 saved 事件,父组件的 $refresh 会被调用,父组件会刷新。
除了传入 $refresh,你还可以传入平时会传给 wire:click 之类指令的任意方法。下面是调用 close() 方法的示例,该方法可能用于关闭模态对话框等:
<livewire:edit-post @saved="close">如果子组件在派发时附带了参数,例如 $this->dispatch('saved', postId: 1),你可以用以下语法将这些值转发给父组件方法:
<livewire:edit-post @saved="close($event.detail.postId)">使用 JavaScript 与事件交互
当你从应用内的 JavaScript 与 Livewire 事件系统交互时,它会变得更加强大。这样应用中的任意其他 JavaScript 都能与页面上的 Livewire 组件通信。
在组件脚本中监听事件
你可以在组件模板中通过 <script> 标签轻松监听 post-created 事件,如下所示:
<script>
this.$on('post-created', () => {
//
});
</script>上面的代码片段会在注册它的组件内监听 post-created。如果该组件已不在页面上,事件监听器将不再被触发。
了解如何在 Livewire 组件中使用 JavaScript →
从组件脚本派发事件
此外,你也可以在组件的 <script> 标签内派发事件,如下所示:
<script>
this.$dispatch('post-created');
</script>运行上述脚本时,post-created 事件会派发到定义该脚本的组件。
若只想把事件派发到脚本所在的组件,而不发给页面上的其他组件(阻止事件「冒泡」),可以使用 dispatchSelf():
this.$dispatchSelf('post-created');你可以通过向 dispatch() 传入对象作为第二个参数,为事件附加任意参数:
<script>
this.$dispatch('post-created', { refreshPosts: true });
</script>现在,你可以在 Livewire 类以及其他 JavaScript 事件监听器中访问这些事件参数。
下面是在 Livewire 类中接收 refreshPosts 参数的示例:
use Livewire\Attributes\On;
// ...
#[On('post-created')]
public function handleNewPost($refreshPosts = false)
{
//
}你也可以在 JavaScript 事件监听器中通过事件的 detail 属性访问 refreshPosts 参数:
<script>
this.$on('post-created', (event) => {
let refreshPosts = event.detail.refreshPosts
// ...
});
</script>了解如何在 Livewire 组件中使用 JavaScript →
从全局 JavaScript 监听 Livewire 事件
或者,你可以在应用中的任意脚本里使用 Livewire.on 全局监听 Livewire 事件:
<script>
document.addEventListener('livewire:init', () => {
Livewire.on('post-created', (event) => {
//
});
});
</script>上面的代码片段会监听页面上任意组件派发的 post-created 事件。
如果出于任何原因希望移除该事件监听器,可以使用返回的 cleanup 函数:
<script>
document.addEventListener('livewire:init', () => {
let cleanup = Livewire.on('post-created', (event) => {
//
});
// Calling "cleanup()" will un-register the above event listener...
cleanup();
});
</script>在 Alpine 中使用事件
因为 Livewire 事件底层就是普通的浏览器事件,你可以用 Alpine 监听甚至派发它们。
在 Alpine 中监听 Livewire 事件
例如,我们可以轻松地用 Alpine 监听 post-created 事件:
<div x-on:post-created="..."></div>上面的代码片段会监听挂载了 x-on 指令的 HTML 元素的任意子 Livewire 组件派发的 post-created 事件。
若要监听页面上任意 Livewire 组件的该事件,可为监听器添加 .window:
<div x-on:post-created.window="..."></div>若要访问随事件发送的附加数据,可以使用 $event.detail:
<div x-on:post-created="notify('New post: ' + $event.detail.title)"></div>Alpine 文档提供了关于监听事件的更多信息。
从 Alpine 派发 Livewire 事件
从 Alpine 派发的任意事件都可以被 Livewire 组件拦截。
例如,我们可以轻松地从 Alpine 派发 post-created 事件:
<button x-on:click="$dispatch('post-created')">...</button>与 Livewire 的 dispatch() 方法类似,你可以通过方法的第二个参数随事件传递附加数据:
<button x-on:click="$dispatch('post-created', { title: 'Post Title' })">...</button>要了解更多关于使用 Alpine 派发事件的内容,请参阅 Alpine 文档。
TIP
你可能并不需要事件
如果你是用事件从子组件调用父组件的行为,可以改为在 Blade 模板中用 $parent 直接从子组件调用 action。例如:
<button wire:click="$parent.showCreatePostForm()">Create Post</button>直接派发到另一个组件
若要用事件在页面上的两个组件之间直接通信,可以使用 dispatch()->to() 修饰符。
下面是 post.create 组件将 post-created 事件直接派发到 dashboard 组件的示例,会跳过其他监听该事件的组件:
<?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 // resources/views/components/post/⚡create.blade.php
use Livewire\Component;
new class extends Component {
public function save()
{
// ...
$this->dispatch('post-created')->to(self: true);
}
};从 Blade 模板派发事件
你可以使用 $dispatch JavaScript 函数直接从 Blade 模板派发事件。当你希望从用户交互(例如按钮点击)触发事件时,这很有用:
<button wire:click="$dispatch('show-post-modal', { id: {{ $post->id }} })">
EditPost
</button>在此示例中,点击按钮时会派发带有指定数据的 show-post-modal 事件。
若要将事件直接派发到另一个组件,可以使用 $dispatchTo() JavaScript 函数:
<button wire:click="$dispatchTo('posts', 'show-post-modal', { id: {{ $post->id }} })">
EditPost
</button>在此示例中,点击按钮时,show-post-modal 事件会直接派发到 Posts 组件。
测试已派发的事件
要测试组件派发的事件,在 Livewire 测试中使用 assertDispatched() 方法。该方法会检查在组件生命周期中是否已派发特定事件:
<?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
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 为网页提供实时功能。
WARNING
安装 Laravel Echo 是前置条件
此功能假定你已安装 Laravel Echo,且 window.Echo 对象在应用中全局可用。关于安装 Echo 的更多信息,请参阅 Laravel Echo 文档。
监听 Echo 事件
假设 Laravel 应用中有一个名为 OrderShipped 的事件:
<?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');
}
}你可能在应用的其他部分像这样派发该事件:
use App\Events\OrderShipped;
OrderShipped::dispatch();如果仅用 Laravel Echo 在 JavaScript 中监听该事件,大致如下:
Echo.channel('orders')
.listen('OrderShipped', e => {
console.log(e.order)
})假定你已安装并配置好 Laravel Echo,就可以在 Livewire 组件内监听该事件。
下面是一个 order-tracker 组件的示例,它监听 OrderShipped 事件,以便向用户展示新订单的视觉提示:
<?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 // 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;
}
// ...
};或者,如果你更喜欢,也可以使用动态事件名语法:
#[On('echo:orders.{order.id},OrderShipped')]
public function notifyNewOrder()
{
$this->showNewOrderNotification = true;
}若需要访问事件载荷,可以通过传入的 $event 参数获取:
#[On('echo:orders.{order.id},OrderShipped')]
public function notifyNewOrder($event)
{
$order = Order::find($event['orderId']);
//
}使用 broadcastAs() 自定义广播事件名
默认情况下,Laravel 使用事件类名进行广播。不过,你可以在事件类中实现 broadcastAs() 方法来自定义广播事件名。
例如,如果你有一个 ScoreSubmitted 事件,但希望将其广播为 score.submitted:
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ScoreSubmitted implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function broadcastOn()
{
return new Channel('scores');
}
public function broadcastAs(): string
{
return 'score.submitted';
}
}在 Livewire 组件中监听该事件时,应使用 broadcastAs() 返回的自定义广播名称,而不是类名。重要: 使用自定义广播名称时,必须以点(.)作为前缀,以便与带命名空间的事件类名区分开。这是 Laravel Echo 约定:
<?php
namespace App\Livewire;
use Livewire\Attributes\On;
use Livewire\Component;
class ScoreBoard extends Component
{
public $scores = [];
#[On('echo:scores,.score.submitted')]
public function handleScoreSubmitted($event)
{
$this->scores[] = $event['score'];
}
}在上面的示例中,Livewire 组件监听的是 .score.submitted(带点前缀的自定义广播名称),而不是 ScoreSubmitted(类名)。点前缀告诉 Laravel Echo 不要在事件名前追加应用的命名空间(App\Events)。
你也可以将自定义广播名称与动态频道名一起使用:
#[On('echo:scores.{game.id},.score.submitted')]
public function handleScoreSubmitted($event)
{
$this->scores[] = $event['score'];
}私有频道与 Presence 频道
你也可以监听广播到私有频道和 Presence 频道的事件:
INFO
继续之前,请确保已为广播频道定义了 <a href="https://laravel.com/docs/master/broadcasting#defining-authorization-callbacks">Authentication Callbacks</a>。
<?php // resources/views/components/⚡order-tracker.blade.php
use Livewire\Component;
new class extends Component {
public $showNewOrderNotification = false;
public function getListeners()
{
return [
// Public Channel
"echo:orders,OrderShipped" => 'notifyNewOrder',
// Private Channel
"echo-private:orders,OrderShipped" => 'notifyNewOrder',
// Presence Channel
"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;
}
};