用React、Laravel和WebSockets构建一个实时的聊天应用程序

408 阅读13分钟

Building a realtime chat app with React, Laravel, and WebSockets

你每天都在使用实时通信。它是发送方和接收方之间同时进行的信息交流,延迟几乎为零。互联网、座机、移动/手机、即时通讯(IM)、互联网中继聊天、视频会议、电话会议和机器人远程呈现都是实时通信系统的例子。

在本教程中,你将学习如何使用React.js、Laravel和Ably构建一个实时公共聊天应用程序。你将使用React.js来构建前端/UI,使用Laravel与Ably实时API交互,以促进实时通信。互联网上的任何人都将能够使用这个应用程序向公共聊天室发布信息,并与其他连接的用户匿名交谈。通过构建这种应用,你将了解到构建需要实时数据传输的应用的相关概念。

这个项目的全部代码可以在这个GitHub repo)中找到。

实时聊天应用程序的架构

在开始之前,你应该先熟悉本教程中用来构建聊天应用程序的技术栈。

React.js

React.js是一个开源的JavaScript库,用于构建反应式用户界面,通常用于构建客户端渲染的单页应用程序。将其与TypeScript集成是非常容易的。基于这个教程,你也可以在Next.js中执行同样的一组动作,Next.js是一个元框架,用于开发通用的React.js应用程序,既是客户端也是服务器端渲染的。

拉威尔

Laravel是一个基于PHP的网络应用程序框架,具有丰富的表现力,优雅的语法。Web开发社区喜欢Laravel,因为它提供了一个惊人的开发者体验,并支持先进的概念,如队列,依赖注入,单元测试,和集成测试。Laravel是构建实时应用程序的最佳选择, 而且它已经支持Ably通过WebSocket连接 "广播 "你的服务器端Laravel事件.

Ably

Ably是一个pub/sub消息平台, 为全球数百万同时连接的设备提供实时同步的数字体验.Ably提供了WebSockets、流简历、历史记录、存在感和可管理的第三方集成,使其能够简单地建立、扩展和提供大规模的数字实时体验。

实时聊天应用程序的架构会是这样的。

Building a realtime chat app with React, Laravel, and WebSockets

聊天应用程序的工作流程架构

设置您的Ably账户


首先,如果您没有Ably账户,您需要创建一个账户

接下来,你需要从Ably仪表板上获取API密钥。

1.访问你的应用仪表板,点击创建新应用

2.2.一旦应用程序被创建,复制私人API密钥。妥善保管该密钥;你将使用它来验证Laravel和React.js中的Ably服务。

Building a realtime chat app with React, Laravel, and WebSockets

来自Ably仪表板的私人密钥

3.在你的Ably应用设置中启用Pusher协议支持,以便在Ably中使用Pusher协议。你可以在设置标签下的协议适配器设置中找到这一功能。如果你被问及创建默认命名空间,你应该点击No thanks

Building a realtime chat app with React, Laravel, and WebSockets

更新Ably设置

就这样了。你的Ably账户已经准备好支持实时消息传递。

设置环境


以下是你需要开始使用的东西。

先决条件

-Node.js:本教程使用Node v16.14.0。

-PHP。本教程使用 PHP v7.4.3

-**[Composer](ghost.ably.com/p/d97576e9-… v2.1.0。

设置项目

你需要一个主目录来存放前端(React.js)和后端(Laravel)的代码。打开你的终端,导航到一个你选择的路径,并通过运行以下命令创建一个目录chat-app-react-laravel-ably

mkdir chat-app-react-laravel-ably

chat-app-react-laravel-ably 目录中, 你将安装Laravel和React.js两个项目.

设置后端


首先建立聊天应用程序的后台部分。

然后在你的终端,执行以下命令,在backend 目录中创建一个Laravel项目。

composer create-project laravel/laravel backend

接下来, 一旦上述命令被执行, 在你的终端运行以下命令来启动你的Laravel项目:

cd backend
php artisan serve

最后, 在你的浏览器中打开localhost:8000, 你会看到你的Laravel项目正在运行:

Building a realtime chat app with React, Laravel, and WebSockets

Laravel应用程序主页

设置广播


在广播任何事件之前, 你需要注册App\Providers\BroadcastServiceProvider 。要做到这一点, 打开config/app.php 文件, 然后在providers 数组中取消对BroadcastServiceProvider 的注释:

'providers' => [
...
App\Providers\BroadcastServiceProvider::class,
...
]

这个BroadcastServiceProvider 类包含注册广播授权路线和回调的必要代码。

接下来,由于你想使用Ably来广播你的事件,在终端运行以下命令来安装Ably PHP SDK

composer require ably/ably-php

你还应该在config/broadcasting.php 文件中配置你的Ably凭证。这个文件中已经包含了一个Ably配置的例子,允许你快速指定你的密钥。

然后将ABLY_KEY 环境变量添加到.env 文件中,并用你在Ably仪表板上注册的Ably应用程序的密钥替换your-ably-key

ABLY_KEY=your-ably-key

最后, 在.env 文件中把广播驱动(BROADCAST_DRIVER)改为ably:

BROADCAST_DRIVER=ably

你的Laravel项目现在已经准备好开始广播事件了.

在Laravel中用WebSockets广播事件


在编写广播事件到你的React.js前端的代码之前, 重要的是要知道Laravel的广播是如何工作的。

Laravel的事件广播允许你使用基于WebSocket的驱动方法,将服务器端的事件广播到客户端的应用程序。你可以在客户端使用Laravel Echonpm包来消费这些事件.

Building a realtime chat app with React, Laravel, and WebSockets

事件是通过通道广播的.你的应用程序可以通过/不通过认证或授权来订阅这些通道, 这取决于这些通道是公开的还是私有的.

要从Laravel发送事件, 在你的终端运行以下命令来创建一个事件文件:

php artisan make:event MessageEvent

该命令将在app/Events 目录中创建一个MessageEvent.php 文件。

接下来, 打开app/Events/MessageEvent.php 文件,用以下代码替换其内容。

<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str;
// 1
class MessageEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
private $user, $message;
// 2
public function __construct($user, $message)
{
$this->user = $user;
$this->message = $message;
}
// 3
public function broadcastWith()
{
return [
'id' => Str::orderedUuid(),
'user' => $this->user,
'message' => $this->message,
'createdAt' => now()->toDateTimeString(),
];
}
// 4
public function broadcastAs()
{
return 'message.new';
}
// 5
public function broadcastOn()
{
return new Channel('public.room');
}
}

以下是上述代码中的具体内容。

1.你定义了一个MessageEvent 类,实现了`ShouldBroadcast`接口。

2.你为MessageEvent 类定义了一个构造函数(__construct),在创建MessageEvent 事件对象时,你将把用户($user)和消息$message)的变量传给它。

3.3.你定义一个broadcastWith 方法,返回与你想广播的消息有关的数据数组。默认情况下,一个事件类的所有public 属性被自动序列化并作为事件的有效载荷进行广播。所以这个方法让你对你在事件的通道上广播的数据有更多的控制。

4.你通过定义一个broadcastAs 方法来指定广播名称为message.new 。这个方法是不需要的, 因为, 默认情况下, Laravel是用事件的类名来广播事件的.

5.5. 你定义了一个broadcastOn 方法, 负责返回事件应该广播的频道.在这种情况下, 事件将被广播在一个公共频道(Channel),名为public.room 。如果你想在一个私人通道上广播一个事件,你可以使用PrivateChannel ,而不是Channel

接收来自React.js前台的请求


在这个聊天应用项目中, 前端将发送一个POST请求到Laravel的后台.在收到请求后, Laravel将启动MessageEvent ,并使用Ably指定的广播驱动程序广播该事件。

要接收来自前端的API请求, 打开routes/api.php 文件, 用以下代码替换其内容:

<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Events\MessageEvent;
// 1
Route::post('new-message', function (Request $request) {
// 2
event(new MessageEvent($request->user, $request->message));
return 'ok';
});

在上面的代码中, 以下是具体内容:

1.你定义一个API路由(`new-message`),你将能够向其发送POST请求。这个API路由将在localhost:8000/api/new-message]可用。

2.你通过传递用户($request->user)和消息($request->message)变量作为参数来启动MessageEvent 事件。

最后,为了检查代码到目前为止是否正常工作,尝试使用Postman或其他工具向localhost:8000/api/new-message端点发送一个POST请求,并将usermessage 作为有效载荷。你应该收到一个ok 响应。如果你没有收到一个ok 响应, 请仔细查看教程的步骤,以确保你已经正确地遵循它们。

项目的Laravel部分就到此为止。接下来, 你需要设置一个React.js应用程序来监听Laravel后端广播的事件。

设置React.js前台


现在你已经完全建立了你的Laravel项目, 是时候建立React.js的前端应用了.

由于你当前的终端窗口是为Laravel项目服务的, 打开另一个终端窗口,从项目的根目录执行以下命令(chat-app-react-laravel-ably),使用著名的create-react-app创建一个React.js项目。

npx create-react-app frontend

最后, 一旦安装完成, 导航到frontend 目录并通过在终端运行以下命令来启动React.js开发服务器:

cd frontend
npm start

该命令将在3000端口启动开发服务器,并将你带到localhost:3000。React.js网站的第一个视图将看起来像这样。

Building a realtime chat app with React, Laravel, and WebSockets

React.js网站主页

安装前端软件包


你需要安装以下软件包来创建聊天应用程序的前端。

- axios:这个包允许你在你的客户端应用程序中进行HTTP请求。

- Laravel Echo**:**这个JavaScript库允许你订阅频道并监听由你的服务器端广播驱动所广播的事件。

-pusher-js: 你可能会想 "我用Ably来广播我的事件,为什么还需要pusher-js?"这是因为Ably包括一个Pusher协议适配器,让你在客户端应用程序中监听事件时使用Pusher协议。

Control-C关闭React.js开发服务器,然后执行以下命令,为你的React.js应用程序安装所需的模块。

npm install axios laravel-echo pusher-js

一旦安装完成,在.env 文件中添加REACT_APP_MIX_ABLY_PUBLIC_KEY 环境变量。你的Ably公钥是你的Ably密钥中出现在: 字符之前的部分。

REACT_APP_MIX_ABLY_PUBLIC_KEY=<your-ably-public-key>
REACT_APP_API_BASE_URL=http://localhost:8000/api

在上面的配置中, 你已经指定了REACT_APP_MIX_ABLY_PUBLIC_KEYREACT_APP_API_BASE_URL 。重要的是要在环境变量前加上REACT_APP_ ,这样它们就可以被create-react-app使用。

设置Laravel Echo


你必须设置Laravel Echo,它才能帮助你订阅频道和监听事件。打开src/App.js 文件并输入以下代码:

// 1
import PublicMessagesPage from './components/PublicMessagesPage';
// 2
export default function App() {
return <PublicMessagesPage />;
}

在上面的代码中, 这就是正在发生的事情:

1.你导入了PublicMessagesPage 组件。

2.你在`App`组件中渲染PublicMessagesPage 组件。

建立前台用户界面


现在您将构建公共聊天主页和消息框组件,它们构成了应用程序的前端用户界面。

构建公共聊天主页

首先,在src 目录中创建一个components 目录。然后,在components 目录中,创建一个PublicMessagesPage.js 文件,并在其中添加以下代码。

// 1
import React, { useState, useEffect } from 'react';
import Axios from 'axios';
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
import Messagebox from './Messagebox';
// 2
export default function PublicMessagesPage() {
// 3
const [user, setUser] = useState('');
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
async function handleSendMessage(e) {
// TODO
}
// 4
return (
<div>
<div>
<div>
<h1>Public Space</h1>
<p>Post your random thoughts for the world to see</p>
</div>
<div>
{messages.map((message) => (
<Messagebox key={message.id} message={message} />
))}
</div>
<div>
<form onSubmit={(e) => handleSendMessage(e)}>
<input
type="text"
placeholder="Set your username"
value={user}
onChange={(e) => setUser(e.target.value)}
required
/>
<div>
<input
type="text"
placeholder="Type your message..."
value={message}
onChange={(e) => setMessage(e.target.value)}
required
/>
<button onClick={(e) => handleSendMessage(e)}>Send</button>
</div>
</form>
</div>
</div>
</div>
);
}

这就是上述代码中所发生的事情。

1.你导入所需的NPM包。

2.2.你定义PublicMessagesPage 功能组件。

3.你使用useState React挂钩为PublicMessagesPage 组件定义state 变量。

-user 存储当前用户的名字

-message 存储当前输入的信息

-messages 存储当前会话中发送和接收的所有消息。

4.你返回公共信息页面的JSX模板。

>**注意:**应用程序的造型部分被跳过了,因为现在理解功能更重要,而应用程序的造型可以根据你的喜好来完成。

接下来,你需要实现Messagebox 组件。

创建消息框组件

首先,在components 目录中,创建一个Messagebox.js 文件,并在其中添加以下代码。

// 1
export default function Messagebox({ message }) {
const formatDate = (value) => {
if (!value) return '';
return new Date(value).toLocalTimeString();
};
// 2
return (
<div>
<div>
<p>
<b>{message.user}</b>
</p>
<p>{message.message}</p>
<p>{formatDate(message.createdAt)}</p>
</div>
</div>
);
}

以下是上述代码的具体内容。

1.你定义了一个无状态的功能组件Messagebox ,并将message 对象作为其参数传递。

2.你返回该Messagebox 组件的HTML模板。

在这一点上,保存你的进度,并通过在终端运行以下命令重启React.js开发服务器。

npm start

访问localhost:3000,根据你对应用程序的风格设计,你会得到这样的结果。

Building a realtime chat app with React, Laravel, and WebSockets

聊天应用程序主页

发送消息


建立了前端用户界面后,下一步是启用消息发送。在src/components/PublicMessagesPage.js 文件中,你需要实现handleSendMessage 方法。因此,首先要更新handleSendMessage 函数,添加以下代码。

async handleSendMessage(e) {
// 1
e.preventDefault();
// 2
if (!user) {
alert('Please add your username');
return;
}
// 3
if (!message) {
alert('Please add a message');
return;
}
try {
// 4
await Axios.post('/new-message', {
user: user,
message: message,
});
} catch (error) {
console.error(error);
}
}

以下是上述代码的具体内容。

1.你使用`e.preventDefault()`方法来防止页面在表单提交后重新加载。

2.你验证user 状态变量是否为空。如果是空的,你提醒用户添加他们的用户名。

3.3.你还要验证message 状态变量是否为空。如果是空的,你就提醒用户添加信息。

4.4.验证成功后,向/new-message API端点发出POST请求(Axios.post),并将usermessage 状态变量作为有效载荷发送。

一旦向Laravel端点发出POST请求,MessageEvent 事件就会被触发。所以接下来, 你需要在你的React.js应用程序中监听这个事件。

监听事件


为了监听事件, 在src/components/PublicMessagesPage.js 文件中的PublicMessagesPage 组件上添加useEffect React 钩子。

// 1
useEffect(() => {
// 2
Axios.defaults.baseURL = process.env.REACT_APP_API_BASE_URL;
// 3
const echo = new Echo({
broadcaster: 'pusher',
key: process.env.REACT_APP_MIX_ABLY_PUBLIC_KEY,
wsHost: 'realtime-pusher.ably.io',
wsPort: 443,
disableStats: true,
encrypted: true,
});
// 4
echo
.channel('public.room')
.subscribed(() => {
console.log('You are subscribed');
})
// 5
.listen('.message.new', (data) => {
// 6
setMessages((oldMessages) => [...oldMessages, data]);
setMessage('');
});
}, []);

在上面的代码中,以下是具体内容。

1.你定义了useEffect React钩子,在`PublicMessagesPage`组件被安装后立即被调用。

2.2.你定义了Axios包的基本URL (Axios.defaults.baseURL),它在此基础上向后端API发出HTTP请求。

3.3.你创建了一个新的Echo 实例并订阅了后台的public.room 频道。

4.4.你订阅公共频道(public.room),在该频道上广播事件。

5.5.你监听message.new 事件,并传递回调函数,接收作为事件响应的data 发送。由于你已经使用broadcastAs 方法定义了一个自定义的广播名称,你需要在事件名称前添加. 字符。这一添加指示Echo ,不要将应用程序的名称空间预置到事件名称中。

5.你更新messages 状态变量,将新的消息有效载荷(data)添加到状态中的现有消息(oldMessages)中。

最后,保存你的进度并重新加载React.js开发服务器。现在你就可以向公共聊天室发送消息了。

Building a realtime chat app with React, Laravel, and WebSockets

在聊天应用程序中发送消息

测试应用程序


为了测试您的聊天应用程序,在至少两个浏览器标签或窗口中打开localhost:3000,并尝试通过设置不同的用户名发送消息。以下是该应用程序的表现。

Building a realtime chat app with React, Laravel, and WebSockets

两个用户之间的消息发送

总结


这就是了!在本教程中,你学会了如何使用React.js、Laravel和Ably创建一个实时聊天应用程序。你还测试了你的应用程序是否正常运行。根据你的要求,你可以在很棒的Ably文档中发现如何为你的聊天应用添加更多的功能。听到你将如何在你的应用程序中使用Ably,我很兴奋。

本教程的全部源代码可在这个GitHub资源库中找到。