廣播系統(tǒng)
廣播系統(tǒng)
廣播系統(tǒng)
簡(jiǎn)介
在現(xiàn)代的 web 應(yīng)用程序中, WebSockets 被用來實(shí)現(xiàn)實(shí)時(shí)、即時(shí)更新的用戶接口。當(dāng)服務(wù)器上的數(shù)據(jù)更新后,更新信息會(huì)通過 WebSocket 連接發(fā)送到客戶端等待處理。相比于不停地輪詢應(yīng)用程序,這是一種更加可靠和高效的選擇。
為了幫助你構(gòu)建這類應(yīng)用, Laravel 將通過 WebSocket 連接來使「廣播」 事件 變得更加輕松。 廣播 Laravel 事件允許你在服務(wù)端和客戶端 JavaScript 應(yīng)用程序間共享相同的事件名。
{注} 在深入了解事件廣播之前,請(qǐng)確認(rèn)你已閱讀所有關(guān)于 Laravel 事件和監(jiān)聽器 的文檔。
配置
所有關(guān)于事件廣播的配置都保存在 config/broadcasting.php
配置文件中。 Laravel 自帶了幾個(gè)廣播驅(qū)動(dòng): Pusher 、 Redis , 和一個(gè)用于本地開發(fā)與調(diào)試的 log
驅(qū)動(dòng)。另外,還有一個(gè) null
驅(qū)動(dòng)允許你完全關(guān)閉廣播系統(tǒng)。每一個(gè)驅(qū)動(dòng)的示例配置都可以在 config/broadcasting.php
配置文件中找到。
廣播服務(wù)提供者
在對(duì)事件進(jìn)行廣播之前,你必須先注冊(cè) App\Providers\BroadcastServiceProvider
。對(duì)于一個(gè)新建的 Laravel 應(yīng)用程序,你只需要在 config/app.php
配置文件的 providers
數(shù)組中取消對(duì)該提供者的注釋即可。該提供者將允許你注冊(cè)廣播授權(quán)路由和回調(diào)。
CSRF 令牌
Laravel Echo 需要訪問當(dāng)前會(huì)話的 CSRF 令牌。你應(yīng)當(dāng)驗(yàn)證你的應(yīng)用程序的 head
HTML 元素是否定義了包含 CSRF 令牌的 meta
標(biāo)簽:
<meta name="csrf-token" content="{{ csrf_token() }}">
對(duì)驅(qū)動(dòng)的要求
Pusher
如果你使用 Pusher 來對(duì)事件進(jìn)行廣播,請(qǐng)用 Composer 包管理器來安裝 Pusher PHP SDK :
composer require pusher/pusher-php-server "~3.0"
然后,你需要在 config/broadcasting.php
配置文件中配置你的 Pusher 證書。該文件中已經(jīng)包含了一個(gè) Pusher 示例配置,你可以快速地指定你的 Pusher key 、secret 和 application ID。 config/broadcasting.php
文件的 pusher
配置項(xiàng)同時(shí)也允許你指定 Pusher 支持的額外 options
,例如 cluster:
'options' => [ 'cluster' => 'eu', 'encrypted' => true ],
當(dāng) Pusher 和 Laravel Echo 一起使用時(shí),你應(yīng)該在 resources/assets/js/bootstrap.js
文件中實(shí)例化 Echo 對(duì)象時(shí)指定 pusher
作為所需要的 broadcaster :
import Echo from "laravel-echo"window.Pusher = require('pusher-js'); window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key' });
Redis
如果你使用 Redis 廣播器,請(qǐng)安裝 Predis 庫(kù):
composer require predis/predis
Redis 廣播器會(huì)使用 Redis 的 發(fā)布 / 訂閱 特性來廣播消息;盡管如此,你仍需將它與能夠從 Redis 接收消息的 WebSocket 服務(wù)器配對(duì)使用以便將消息廣播到你的 WebSocket 頻道上去。
當(dāng) Redis 廣播器發(fā)布一個(gè)事件的時(shí)候,該事件會(huì)被發(fā)布到它指定的頻道上去,傳輸?shù)臄?shù)據(jù)是一個(gè)采用 JSON 編碼的字符串。該字符串包含了事件名、 data
數(shù)據(jù)和生成該事件 socket ID 的用戶(如果可用的話)。
Socket.IO
如果你想將 Redis 廣播器 和 Socket.IO 服務(wù)器進(jìn)行配對(duì),你需要在你的應(yīng)用程序中引入 Socket.IO JavaScript 客戶端庫(kù)。你可以通過 NPM 包管理器進(jìn)行安裝:
npm install --save socket.io-client
然后,你需要在實(shí)例化 Echo 時(shí)指定 socket.io
連接器和 host
。
import Echo from "laravel-echo"window.io = require('socket.io-client'); window.Echo = new Echo({ broadcaster: 'socket.io', host: window.location.hostname + ':6001' });
最后,你需要運(yùn)行一個(gè)與 Laravel 兼容的 Socket.IO 服務(wù)器。 Laravel 官方并沒有內(nèi)置 Socket.IO 服務(wù)器實(shí)現(xiàn);不過,可以選擇一個(gè)由社區(qū)驅(qū)動(dòng)維護(hù)的項(xiàng)目 tlaverdure/laravel-echo-server ,目前托管在 GitHub 。
對(duì)隊(duì)列的要求
在開始廣播事件之前,你還需要配置和運(yùn)行 隊(duì)列監(jiān)聽器 。所有的事件廣播都是通過隊(duì)列任務(wù)來完成的,因此應(yīng)用程序的響應(yīng)時(shí)間不會(huì)受到明顯影響。
概念綜述
Laravel 的事件廣播允許你使用基于驅(qū)動(dòng)的 WebSockets 將服務(wù)端的 Laravel 事件廣播到客戶端的 JavaScript 應(yīng)用程序。當(dāng)前的 Laravel 自帶了 Pusher 和 Redis 驅(qū)動(dòng)。通過使用 Laravel Echo 的 Javascript 包,我們可以很方便地在客戶端消費(fèi)事件。
事件通過「頻道」來廣播,這些頻道可以被指定為公開或私有的。任何訪客都可以不經(jīng)授權(quán)或認(rèn)證訂閱一個(gè)公開頻道;然而,如果想要訂閱一個(gè)私有頻道,那么該用戶必須通過認(rèn)證,并獲得該頻道的授權(quán)。
使用示例程序
在深入了解事件廣播的每個(gè)組件之前,讓我們先用一個(gè)電子商務(wù)網(wǎng)站作為例子來概覽一下。我們不會(huì)討論配置 Pusher 或者 Laravel Echo 的細(xì)節(jié),這些會(huì)在本文檔的其它章節(jié)里詳細(xì)討論。
在我們的應(yīng)用程序中,我們假設(shè)有一個(gè)允許用戶查看訂單配送狀態(tài)的頁(yè)面。有一個(gè) ShippingStatusUpdated
事件會(huì)在配送狀態(tài)更新時(shí)被觸發(fā):
event(new ShippingStatusUpdated($update));
ShouldBroadcast
接口
當(dāng)用戶在查看自己的訂單時(shí),我們不希望他們必須通過刷新頁(yè)面才能看到狀態(tài)更新。我們希望一旦有更新時(shí)就主動(dòng)將更新信息廣播到客戶端。所以,我們必須標(biāo)記 ShippingStatusUpdated
事件實(shí)現(xiàn) ShouldBroadcast
接口。這會(huì)讓 Laravel 在事件被觸發(fā)時(shí)廣播該事件:
<?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Queue\SerializesModels; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class ShippingStatusUpdated implements ShouldBroadcast{ /** * 有關(guān)配送狀態(tài)更新的信息。 * * @var string */ public $update; }
ShouldBroadcast
接口要求事件定義一個(gè) broadcastOn
方法。該方法負(fù)責(zé)指定事件被廣播到哪些頻道。在(通過 Artisan 命令)生成的事件類中,一個(gè)空的 broadcastOn
方法已經(jīng)被預(yù)定義好了,所以我們只需要完成其細(xì)節(jié)即可。我們希望只有訂單的創(chuàng)建者能夠看到狀態(tài)的更新,所以我們要把該事件廣播到與這個(gè)訂單綁定的私有頻道上去:
/** * 獲取事件應(yīng)該廣播的頻道。 * * @return array */ public function broadcastOn(){ return new PrivateChannel('order.'.$this->update->order_id); }
授權(quán)頻道
記住,用戶只有在被授權(quán)之后才能監(jiān)聽私有頻道。我們可以在 routes/channels.php
文件中定義頻道的授權(quán)規(guī)則。在本例中,我們需要對(duì)視圖監(jiān)聽私有 order.1
頻道的所有用戶進(jìn)行驗(yàn)證,確保只有訂單真正的創(chuàng)建者才能監(jiān)聽:
Broadcast::channel('order.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; });
channel
方法接收兩個(gè)參數(shù):頻道名稱和一個(gè)回調(diào)函數(shù),該回調(diào)通過返回 true
或者 false
來表示用戶是否被授權(quán)監(jiān)聽該頻道。
所有的授權(quán)回調(diào)接收當(dāng)前被認(rèn)證的用戶作為第一個(gè)參數(shù),任何額外的通配符參數(shù)作為后續(xù)參數(shù)。在本例中,我們使用 {orderId}
占位符來表示頻道名稱的 「ID」 部分是通配符。
對(duì)事件廣播進(jìn)行監(jiān)聽
接下來,就只剩下在 JavaScript 應(yīng)用程序中監(jiān)聽事件了。我們可以通過 Laravel Echo 來實(shí)現(xiàn)。首先,我們使用 private
方法來訂閱私有頻道。然后,使用 listen
方法來監(jiān)聽 ShippingStatusUpdated
事件。默認(rèn)情況下,事件的所有公有屬性會(huì)被包括在廣播事件中:
Echo.private(`order.${orderId}`) .listen('ShippingStatusUpdated', (e) => { console.log(e.update); });
定義廣播事件
要告知 Laravel 一個(gè)給定的事件需要廣播,只需要在事件類中實(shí)現(xiàn) Illuminate\Contracts\Broadcasting\ShouldBroadcast
接口即可。該接口已被導(dǎo)入到所有由框架生成的事件類中,所以你可以很方便地將它添加到你自己的事件中。
ShouldBroadcast
接口要求你實(shí)現(xiàn)一個(gè)方法: broadcastOn
。 該方法返回一個(gè)頻道或者一個(gè)頻道數(shù)組,事件會(huì)被廣播到這些頻道。這些頻道必須是 Channel
、PrivateChannel
或者 PresenceChannel
的實(shí)例。 Channel
代表任何用戶都可以訂閱的公開頻道, 而 PrivateChannels
和 PresenceChannels
則代表需要 頻道授權(quán) 的私有頻道:
<?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Queue\SerializesModels; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class ServerCreated implements ShouldBroadcast{ use SerializesModels; public $user; /** * 創(chuàng)建一個(gè)新的事件實(shí)例。 * * @return void */ public function __construct(User $user) { $this->user = $user; } /** *獲得事件廣播的頻道。 * * @return Channel|array */ public function broadcastOn() { return new PrivateChannel('user.'.$this->user->id); } }
然后,你只需要像你平時(shí)那樣 觸發(fā)事件 。一旦事件被觸發(fā),一個(gè) 隊(duì)列任務(wù) 會(huì)自動(dòng)廣播事件到你指定的廣播驅(qū)動(dòng)上。
廣播名稱
Laravel 默認(rèn)會(huì)使用事件的類名作為廣播名稱來廣播事件。不過,你也可以在事件類中定義一個(gè) broadcastAs
方法來自定義廣播名稱:
/** * 事件的廣播名稱。 * * @return string */ public function broadcastAs(){ return 'server.created'; }
如果你使用了 broadcastAs
方法來自定義廣播名稱,你應(yīng)當(dāng)確保在你注冊(cè)監(jiān)聽器時(shí)加上一個(gè) .
的前綴。這將指示 Echo 不要在事件之前添加應(yīng)用程序的命名空間:
.listen('.server.created', function (e) { .... });
廣播數(shù)據(jù)
當(dāng)一個(gè)事件被廣播時(shí),其所有的 public
屬性都會(huì)自動(dòng)序列化并作為事件有效載荷進(jìn)行廣播,這允許你在 JavaScript 應(yīng)用程序中訪問到事件所有的公有數(shù)據(jù)。舉個(gè)例子,如果你的事件有一個(gè)單獨(dú)的包含了一個(gè) Eloquent 模型的公有 $user
屬性,那么事件的廣播有效載荷將會(huì)是:
{ "user": { "id": 1, "name": "Patrick Stewart" ... } }
不過,如果你想更細(xì)粒度地控制你的廣播有效載荷,你可以向你的事件中添加一個(gè) broadcastWith
方法。這個(gè)方法會(huì)返回一個(gè)你想要作為事件有效載荷進(jìn)行廣播的數(shù)據(jù)數(shù)組:
/** * 指定廣播數(shù)據(jù)。 * * @return array */ public function broadcastWith(){ return ['id' => $this->user->id]; }
廣播隊(duì)列
默認(rèn)情況下,每一個(gè)廣播事件都會(huì)被推送到在 queue.php
配置文件中指定的默認(rèn)隊(duì)列連接相應(yīng)的默認(rèn)隊(duì)列中。你可以在事件類中定義一個(gè) broadcastQueue
屬性來自定義廣播器所使用的隊(duì)列。該屬性需要你指定廣播時(shí)你想要用的隊(duì)列名稱:
/** * 事件被推送到的隊(duì)列名稱。 * * @var string */ public $broadcastQueue = 'your-queue-name';
如果你想使用 sync
隊(duì)列而不是默認(rèn)隊(duì)列驅(qū)動(dòng)來廣播事件,你可以實(shí)現(xiàn) ShouldBroadcastNow
接口而不是 ShouldBroadcast
:
<?php use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; class ShippingStatusUpdated implements ShouldBroadcastNow{ // }
廣播條件
有時(shí),你想在給定條件為 true 的情況下才廣播你的事件。你可以通過在事件類中添加一個(gè) broadcastWhen
方法來定義這些條件:
/** * 判定事件是否可以廣播。 * * @return bool */ public function broadcastWhen(){ return $this->value > 100; }
授權(quán)頻道
對(duì)于私有頻道,用戶只有被授權(quán)之后才能監(jiān)聽。實(shí)現(xiàn)過程是用戶向你的 Laravel 應(yīng)用程序發(fā)起一個(gè)攜帶頻道名稱的 HTTP 請(qǐng)求,由你的應(yīng)用程序判斷該用戶是否能夠監(jiān)聽該頻道。在使用 Laravel Echo 時(shí),授權(quán)訂閱私有頻道的 HTTP 請(qǐng)求會(huì)自動(dòng)發(fā)送;盡管如此,你仍需定義相應(yīng)的路由來響應(yīng)這些請(qǐng)求。
定義授權(quán)路由
幸運(yùn)的是,在 Laravel 中我們可以很容易地定義路由來響應(yīng)頻道授權(quán)請(qǐng)求。在 Laravel 自帶的 BroadcastServiceProvider
中,你可以看到對(duì) Broadcast::routes
方法的調(diào)用。該方法會(huì)注冊(cè) /broadcasting/auth
路由來處理授權(quán)請(qǐng)求:
Broadcast::routes();
Broadcast::routes
方法會(huì)自動(dòng)將它的路由置入 web
中間件組中;不過,如果你想自定義指定的屬性,你可以向該方法傳遞一個(gè)路由屬性數(shù)組:
Broadcast::routes($attributes);
自定義授權(quán)端點(diǎn)
默認(rèn)情況下,Echo 將使用 /broadcasting/auth
端點(diǎn)來授權(quán)頻道訪問。 但是,您可以通過將 authEndpoint
配置選項(xiàng)傳遞給 Echo 實(shí)例來指定自己的授權(quán)端點(diǎn):
window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key', authEndpoint: '/custom/endpoint/auth' });
定義授權(quán)回調(diào)
接下來,我們需要定義真正用于處理頻道授權(quán)的邏輯。該邏輯在應(yīng)用程序自帶的 routes/channels.php
文件中完成。在這個(gè)文件中,你可以使用 Broadcast::channel
方法來注冊(cè)頻道授權(quán)回調(diào):
Broadcast::channel('order.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; });
channel
方法接收兩個(gè)參數(shù):頻道名稱和一個(gè)回調(diào)函數(shù),該回調(diào)通過返回 true
或者 false
來表示用戶是否被授權(quán)監(jiān)聽該頻道。
所有的授權(quán)回調(diào)接收當(dāng)前認(rèn)證用戶作為第一個(gè)參數(shù),任何額外的通配符參數(shù)作為后續(xù)參數(shù)。在本例中,我們使用 {orderId}
占位符來表示頻道名稱的 「ID」 部分是通配符。
授權(quán)回調(diào)模型綁定
就像 HTTP 路由一樣,頻道路由也可以利用顯式或隱式 路由模型綁定 。例如,你可以請(qǐng)求接收一個(gè)真正的 Order
模型實(shí)例,而不是字符串或數(shù)字類型的 order ID:
use App\Order; Broadcast::channel('order.{order}', function ($user, Order $order) { return $user->id === $order->user_id; });
授權(quán)回調(diào)驗(yàn)證
私有頻道和在線廣播頻道通過應(yīng)用程序的默認(rèn)授權(quán)驗(yàn)證對(duì)當(dāng)前用戶身份進(jìn)行驗(yàn)證。 如果用戶未經(jīng)過授權(quán)驗(yàn)證,則會(huì)自動(dòng)拒絕通道授權(quán),并且永遠(yuǎn)不會(huì)執(zhí)行授權(quán)回調(diào)。 但是,您可以分配多個(gè)自定義防護(hù),以便在必要時(shí)對(duì)傳入請(qǐng)求進(jìn)行身份驗(yàn)證:
Broadcast::channel('channel', function() { // ... }, ['guards' => ['web', 'admin']])
定義頻道類
如果你的應(yīng)用程序用到了許多不同的頻道,你的 routes/channels.php
文件可能會(huì)變得很龐大。所以,你可以使用頻道類來代替使用閉包授權(quán)頻道。要生成一個(gè)頻道類,請(qǐng)使用 make:channel
Artisan 命令。該命令會(huì)在 App/Broadcasting
目錄中放置一個(gè)新的頻道類。
php artisan make:channel OrderChannel
接下來,在你的 routes/channels.php
文件中注冊(cè)你的頻道:
use App\Broadcasting\OrderChannel; Broadcast::channel('order.{order}', OrderChannel::class);
最后,你可以將頻道的授權(quán)邏輯放入頻道類的 join
方法中。該 join
方法將保存你通常放置在頻道授權(quán)閉包中的相同邏輯。當(dāng)然,你也可以利用頻道模型綁定:
<?php namespace App\Broadcasting; use App\User;use App\Order; class OrderChannel{ /** * 創(chuàng)建一個(gè)新的頻道實(shí)例。 * * @return void */ public function __construct() { // } /** * 認(rèn)證用戶的頻道訪問權(quán)限。 * * @param \App\User $user * @param \App\Order $order * @return array|bool */ public function join(User $user, Order $order) { return $user->id === $order->user_id; } }
{注} 就像 Laravel 中的很多其它類,頻道類會(huì)通過 服務(wù)容器 自動(dòng)解析。因此,你可以在頻道類的構(gòu)造函數(shù)中對(duì)其進(jìn)行所需依賴項(xiàng)的類型提示。
廣播事件
定義好一個(gè)事件且將其標(biāo)記實(shí)現(xiàn) ShouldBroadcast
接口之后,你所要做的僅僅是通過 event
函數(shù)來觸發(fā)該事件。事件分發(fā)器會(huì)識(shí)別出標(biāo)記了實(shí)現(xiàn) ShouldBroadcast
接口的事件,并將其推送到隊(duì)列中進(jìn)行廣播:
event(new ShippingStatusUpdated($update));
只廣播給他人
當(dāng)創(chuàng)建一個(gè)會(huì)用到事件廣播的應(yīng)用程序時(shí),你可以使用 broadcast
函數(shù)來代替 event
。和 event
函數(shù)一樣, broadcast
函數(shù)將事件分發(fā)到服務(wù)端監(jiān)聽器:
broadcast(new ShippingStatusUpdated($update));
不過, broadcast
函數(shù)還有一個(gè)允許你將當(dāng)前用戶排除在廣播接收者之外的 toOthers
方法:
broadcast(new ShippingStatusUpdated($update))->toOthers();
為了更好地理解什么時(shí)候使用 toOthers
方法,讓我們假設(shè)有一個(gè)任務(wù)列表的應(yīng)用程序,用戶可以通過輸入任務(wù)名來新建任務(wù)。要新建任務(wù),你的應(yīng)用程序需要發(fā)起一個(gè)請(qǐng)求到一個(gè) /task
路由,該路由會(huì)廣播任務(wù)的創(chuàng)建,并返回新任務(wù)的 JSON 響應(yīng)。當(dāng)你的 JavaScript 應(yīng)用程序從路由收到響應(yīng)后,它會(huì)直接將新任務(wù)插入到任務(wù)列表中,就像這樣:
axios.post('/task', task) .then((response) => { this.tasks.push(respo });
然而,別忘了,我們還廣播了任務(wù)的創(chuàng)建。如果你的 JavaScript 應(yīng)用程序正在監(jiān)聽該事件以便添加任務(wù)至任務(wù)列表,任務(wù)列表中將出現(xiàn)重復(fù)的任務(wù):一個(gè)來自路由響應(yīng),另一個(gè)來自廣播。你可以通過使用 toOthers
方法告知廣播器不要將事件廣播到當(dāng)前用戶來解決這個(gè)問題。
{注} 為了能調(diào)用
toOthers
方法,你的事件必須使用Illuminate\Broadcasting\InteractsWithSockets
trait 。配置
當(dāng)你初始化 Laravel Echo 實(shí)例的時(shí)候,一個(gè)套接字 ID 會(huì)被分配到該連接。如果你使用了 Vue 和 Axios ,該套接字 ID 會(huì)自動(dòng)地以 X-Socket-ID
頭的方式添加到每一個(gè)傳出請(qǐng)求中。那么,當(dāng)你調(diào)用 toOthers
方法時(shí),Laravel 會(huì)從請(qǐng)求頭中取出套接字 ID ,并告知廣播器不要廣播任何消息到帶有這個(gè)套接字 ID 的連接上。
你如果你沒有使用 Vue 和 Axios ,則需要手動(dòng)配置 JavaScript 應(yīng)用程序來發(fā)送 X-Socket-ID
請(qǐng)求頭。你可以用 Echo.socketId
方法來獲取套接字 ID :
var socketId = Echo.socketId();
接收廣播
安裝 Laravel Echo
Laravel Echo 是一個(gè) JavaScript 庫(kù),有了這個(gè)庫(kù)之后,訂閱頻道監(jiān)聽 Laravel 廣播的事件變得非常容易。你可以通過 NPM 包管理器來安裝 Echo 。在本例中,因?yàn)槲覀儠?huì)使用 Pusher 廣播器,所以我們也會(huì)安裝 pusher-js
包:
npm install --save laravel-echo pusher-js
安裝好 Echo 之后,你就可以在應(yīng)用程序的 JavaScript 中創(chuàng)建一個(gè)全新的 Echo 實(shí)例。做這件事的一個(gè)理想的地方是在 Laravel 框架自帶的 resources/js/bootstrap.js
文件的底部:
import Echo from "laravel-echo" window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key' });
當(dāng)你使用 pusher
連接器來創(chuàng)建一個(gè) Echo 實(shí)例的時(shí)候,還可以指定 cluster
以及連接是否需要加密:
window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key', cluster: 'eu', encrypted: true });
使用現(xiàn)有客戶端實(shí)例
如果您已經(jīng)有一個(gè)希望 Echo 使用的 Pusher 或 Socket.io 客戶端實(shí)例,您可以通過 client
配置選項(xiàng)將其傳遞給 Echo:
const client = require('pusher-js'); window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key', client: client });
對(duì)事件進(jìn)行監(jiān)聽
安裝并實(shí)例化 Echo 之后, 你就可以開始監(jiān)聽事件廣播了。首先,使用 channel
方法獲取一個(gè)頻道實(shí)例,然后調(diào)用 listen
方法來監(jiān)聽指定的事件:
Echo.channel('orders') .listen('OrderShipped', (e) => { console.log(e.order.name); });
如果你想監(jiān)聽私有頻道上的事件,請(qǐng)使用 private
方法。你可以通過鏈?zhǔn)秸{(diào)用 listen
方法來監(jiān)聽單個(gè)頻道上的多個(gè)事件:
Echo.private('orders') .listen(...) .listen(...) .listen(...);
退出頻道
要離開頻道,您可以在 Echo 實(shí)例上調(diào)用 leaveChannel
方法:
Echo.leaveChannel('orders');
如果您想離開私有頻道和在線頻道,您可以調(diào)用 leave
方法:
Echo.leave('orders');
命名空間
你可能已經(jīng)注意到在上面的例子中,我們并沒有為事件類指定完整的命名空間。這是因?yàn)?Echo 會(huì)默認(rèn)事件都在 App\Events
命名空間下。不過,你可以在實(shí)例化 Echo 時(shí)傳遞一個(gè) namespace
配置項(xiàng)來指定根命名空間:
window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key', namespace: 'App.Other.Namespace' });
另外,你可以在使用 Echo 訂閱事件的時(shí)候?yàn)槭录惣由?.
前綴。這樣就可以指定完全限定名稱的類名了:
Echo.channel('orders') .listen('.Namespace.Event.Class', (e) => { // });
Presence 頻道
Presence 頻道構(gòu)建在私有頻道的安全性基礎(chǔ)之上,并提供了額外的特性:獲知誰訂閱了該頻道。這一點(diǎn)使構(gòu)建強(qiáng)大的,協(xié)同的應(yīng)用變得非常容易,比如一個(gè)用戶在瀏覽頁(yè)面時(shí),通知其他正在瀏覽相同頁(yè)面的用戶。
授權(quán) Presence 頻道
所有的 presence 頻道也是私有頻道;因此,用戶必須被 授權(quán)之后才能訪問 。不過,在給 presence 頻道定義授權(quán)回調(diào)函數(shù)時(shí),如果一個(gè)用戶已經(jīng)加入了該頻道,那么不應(yīng)該返回 true
,而應(yīng)該返回一個(gè)關(guān)于該用戶信息的數(shù)組。
由授權(quán)回調(diào)函數(shù)返回的數(shù)據(jù)能夠在你的 JavaScript 應(yīng)用程序中被 presence 頻道事件監(jiān)聽器所使用。如果用戶沒有被授權(quán)加入該 presence 頻道,那么你應(yīng)該返回 false
或者 null
:
Broadcast::channel('chat.{roomId}', function ($user, $roomId) { if ($user->canJoinRoom($roomId)) { return ['id' => $user->id, 'name' => $user->name]; } });
加入 Presence 頻道
你可以使用 Echo 的 join
方法來加入 presence 頻道。 join
方法會(huì)返回一個(gè)實(shí)現(xiàn)了 PresenceChannel
的對(duì)象,通過暴露 listen
方法,允許你訂閱 here
、 joining
和 leaving
事件。
Echo.join(`chat.${roomId}`) .here((users) => { // }) .joining((user) => { console.log(user.name); }) .leaving((user) => { console.log(user.name); });
here
回調(diào)函數(shù)會(huì)在你成功加入頻道后被立即執(zhí)行,并接收一個(gè)包含其他所有當(dāng)前訂閱該頻道的用戶的用戶信息數(shù)組 。 joining
方法會(huì)在新用戶加入頻道時(shí)被執(zhí)行,而 leaving
方法會(huì)在用戶退出頻道時(shí)被執(zhí)行。
廣播到 Presence 頻道
Presence 頻道可以像公開和私有頻道一樣接收事件。使用一個(gè)聊天室的例子,我們可能想把 NewMessage
事件廣播到聊天室的 presence 頻道。要實(shí)現(xiàn)它,我們將從事件的 broadcastOn
方法中返回一個(gè) PresenceChannel
實(shí)例:
/** * 獲得事件廣播的頻道。 * * @return Channel|array */ public function broadcastOn(){ return new PresenceChannel('room.'.$this->message->room_id); }
就像公開或私有事件, presence 頻道事件也可以使用 broadcast
函數(shù)來廣播。同樣的,你也可以使用 toOthers
方法將當(dāng)前用戶排除在廣播接收者之外:
broadcast(new NewMessage($message)); broadcast(new NewMessage($message))->toOthers();
你可以通過 Echo 的 listen
方法來監(jiān)聽 join 事件:
Echo.join(`chat.${roomId}`) .here(...) .joining(...) .leaving(...) .listen('NewMessage', (e) => { // });
客戶端事件
{注} 使用 Pusher 時(shí),如果要發(fā)送客戶端事件,你必須在 應(yīng)用后臺(tái) 的「應(yīng)用設(shè)置」部分啟用「客戶端事件」選項(xiàng)。
有時(shí),你可能希望廣播一個(gè)事件給其它已經(jīng)連接的客戶端,但不通知你的 Laravel 應(yīng)用程序。這在處理「輸入中」這類事情的通知時(shí)尤其有用,比如提醒你應(yīng)用的用戶,另一個(gè)用戶正在給定屏幕上輸入信息。
你可以使用 Echo 的 whisper
方法來廣播客戶端事件:
Echo.private('chat') .whisper('typing', { name: this.user.name });
你可以使用 listenForWhisper
方法來監(jiān)聽客戶端事件:
Echo.private('chat') .listenForWhisper('typing', (e) => { console.log(e.name); });
消息通知
通過與將事件廣播與 消息通知 配對(duì),你的 JavaScript 應(yīng)用程序可以在不刷新頁(yè)面的情況下接收新的消息通知。在此之前,請(qǐng)確保你已經(jīng)讀過了如何使用 廣播通知頻道 的文檔。
配置好使用廣播頻道的消息通知后,你可以使用 Echo 的 notification
方法來監(jiān)聽廣播事件。謹(jǐn)記,頻道名稱應(yīng)該和接收消息通知的實(shí)體類名相匹配:
Echo.private(`App.User.${userId}`) .notification((notification) => { console.log(notification.type); });
在本例中,所有通過 broadcast
頻道發(fā)送到 App\User
實(shí)例的消息通知都會(huì)被回調(diào)接收。一個(gè)針對(duì) App.User.{id}
頻道的授權(quán)回調(diào)函數(shù)已經(jīng)包含在 Laravel 框架內(nèi)置的 BroadcastServiceProvider
中了。