Laravel+vue广播

#背景:

最近需要做个服务端消息推送的事情,首先就考虑到了Laravel自带的广播功能,它是利用redis的发布订阅功能和前端利用node.js实现的一套集合了Websocket 的功能

前置条件:
后端:

1
2
3
4
5
6
7
8
9
1,安装redis扩展
composer require predis/predis
2,在config/app.php中打开注释:
App\Providers\BroadcastServiceProvider::class,
3,在.env中
APP_URL=http://your_domain.com #域名
APP_NAME=YourAppName #默认laravel即可
QUEUE_CONNECTION=redis #redis队列
BROADCAST_DRIVER=redis #broadcast的驱动使用redis

前端:

1
2
3
1,npm Install
2,npm install -g laravel-echo-server #https://github.com/tlaverdure/laravel-echo-server
3,npm install --save socket.io-client

#公有频道
1,后端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php

namespace App\Events;

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

class PublicEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public $msg = '';
public function __construct($msg)
{
$this->msg = $msg;
}

/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('public');
}

// public function broadcastWith()
// {
// return [
// 'data' => 'this is public'
// ];
// }
}

2,前端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
import Echo from 'laravel-echo'

window.io = require('socket.io-client');
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001',
});

window.Echo.channel('public')
.listen('PublicEvent', (e) => {
console.log(e);
});
</script>

3,打开页面在console,即可以看到我们发送的数据

1
2
3
4
5
{msg: "this is public", socket: null}
msg: "this is public"
socket: null
__proto__: Object
}

#私有频道
PrivateEvent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php

namespace App\Events;

use App\Models\UserModel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

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

public $user;
public $userId = '';

/**
* Create a new event instance.
*
* @return void
*/
public function __construct(UserModel $user)
{

$this->user = $user;
$this->userId = 3590;

// dd('user.' . $this->user->u_id);
}

/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
// dd(Auth::user());
return new PrivateChannel('user.' . $this->userId);
}

// 精细化返回的数据,如果加了这个函数,公有属性才不会返回而是以这个函数内放回的字段为主
// public function broadcastWith()
// {
// return [
// 'data' => '2424',
// ];
// }
}

BroadcastServiceProvider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Broadcast;

class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//Broadcast::routes();
Broadcast::routes(['middleware' => 'api']);
#routes()参数如果不配置 默认走的是web中间件
#'middleware' => 'api' 然后广播路由走api中间件,自己也可以定义其他类型的中间件

require base_path('routes/channels.php'); #加载检查权限的广播路由
}
}

routes/channels:#广播路由只针对广播的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/

//占位符模式
\Illuminate\Support\Facades\Broadcast::channel('user.{userId}', function ($user, $userId) {
return true;
}, ['guards' => ['api']]);

#{userId}只是占位符
#$user 闭包默认的第一个参数,授权的用户
#$userId 时间模板的公有属性(此时该值为3590即在PrivateEvent构造函数上所赋的值),也可以是其他的对象
#闭包函数接受多个参数,这个参数由事件里的broadcastOn函数声明的频道决定,如:'user.' . $this->userId . $this->userName 那么就可以接受2个参数userId和userName

//显式的路由模型绑定
\Illuminate\Support\Facades\Broadcast::channel('user.{user2}', function ($user, \App\Models\UserModel $user2) {
\Illuminate\Support\Facades\Log::info($user);
return true;
}, ['guards' => ['api']]);
# 注意这里的占位符{user2} 必须 \App\Models\UserModel $user2 这个变量名一样。否则会报错

前端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>
import Echo from 'laravel-echo'

window.io = require('socket.io-client');
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001',
auth: {
headers: {
Authorization: 'Bearer ' + 'WVP25lQfJFTpCcuA1rmdadgeXQplMOtobWjL4TvkDD8dMbjOX5pBF4kBwiC9'
},
},
});

window.Echo.private('user.3590')
.listen('PrivateEvent', (e) => {
alert(11);
console.log(1111);
console.log(e);
});
</script>

#user.3590 监听的频道
#PrivateEvent 监听的事件
#Authorization 授权,后端会检测如果不对会报错403

页面展示:不出意外会把公有属性全部展示出来,其他的属性不会展示

1
2
3
4
5
6
7
{
user: {…}, userId: 3590, socket: null}
socket: null
user: {u_id: 3590, u_username: "02125", u_job_number: "", u_warehouse_id: 1, u_department_id: 0, …}
userId: 3590
__proto__: Object
}

参考:
https://xueyuanjun.com/post/19505
http://silverd.cn/2018/06/01/laravel-broadcasting-adv.html