You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel聊天应用:如何用Ajax在有新消息时刷新页面/聊天区域

解决Laravel聊天应用实时消息刷新问题

你的问题很典型——普通Ajax只能实现发送方主动刷新,接收方没法实时感知新消息。我给你两种解决方案,从易到难,你可以根据项目需求选择:

方案一:Ajax轮询(快速上手,无需额外服务)

轮询的思路很简单:让接收方的页面定期向服务器发起请求,检查有没有新消息,有就更新页面。结合你的已读状态字段,还能顺便处理消息标记已读的逻辑。

1. 后端准备接口

首先在routes/api.php或者web.php里加两个路由:

// 检查指定会话的未读消息
Route::get('/messages/check-new/{conversationId}', [MessageController::class, 'checkNewMessages']);
// 标记会话内的未读消息为已读
Route::post('/messages/mark-as-read/{conversationId}', [MessageController::class, 'markAsRead']);

然后在MessageController里实现对应的方法:

// 检查新消息
public function checkNewMessages($conversationId)
{
    // 筛选当前用户作为接收方的未读消息
    $newMessages = Message::where('conversation_id', $conversationId)
        ->where('recipient_id', auth()->id())
        ->where('is_read', 0)
        ->get();

    return response()->json([
        'has_new' => $newMessages->isNotEmpty(),
        'messages' => $newMessages,
        'count' => $newMessages->count()
    ]);
}

// 标记消息已读
public function markAsRead($conversationId)
{
    Message::where('conversation_id', $conversationId)
        ->where('recipient_id', auth()->id())
        ->where('is_read', 0)
        ->update(['is_read' => 1]);

    return response()->json(['success' => true]);
}

2. 前端实现轮询逻辑

在你的聊天页面JS里加入这段代码,它会每3秒检查一次新消息:

// 替换成当前会话的ID(可以从Blade模板传递)
const conversationId = {{ $conversation->id }};
let pollInterval;

// 页面加载后启动轮询
$(document).ready(function() {
    startPolling();
});

function startPolling() {
    pollInterval = setInterval(() => {
        $.ajax({
            url: `/messages/check-new/${conversationId}`,
            method: 'GET',
            success: function(res) {
                if (res.has_new) {
                    // 有新消息时,要么调用你已有的刷新容器函数,要么直接追加新消息
                    // 推荐追加,避免全量刷新的卡顿:
                    res.messages.forEach(msg => {
                        const msgHtml = `<div class="message received">${msg.content}</div>`;
                        $('#message-container').append(msgHtml);
                    });

                    // 自动滚动到消息底部
                    $('#message-container').scrollTop($('#message-container')[0].scrollHeight);

                    // 标记这些消息为已读
                    markMessagesAsRead(conversationId);
                }
            },
            error: err => console.error('检查新消息失败:', err)
        });
    }, 3000); // 3秒一次,可根据需求调整
}

function markMessagesAsRead(conversationId) {
    $.ajax({
        url: `/messages/mark-as-read/${conversationId}`,
        method: 'POST',
        data: { _token: '{{ csrf_token() }}' },
        success: () => console.log('消息已标记为已读')
    });
}

// 页面关闭时停止轮询,减少服务器压力
$(window).on('beforeunload', () => clearInterval(pollInterval));

方案二:WebSocket实时推送(体验更优,推荐)

轮询毕竟有延迟,而且频繁请求会浪费服务器资源。用WebSocket可以实现服务器主动给客户端推消息,真正的实时聊天体验。Laravel官方的Laravel Echo配合Pusher或者自建WebSocket服务就能搞定。

1. 配置基础依赖

先安装必要的包:

# 后端Pusher SDK
composer require pusher/pusher-php-server
# 前端Echo和Pusher客户端
npm install laravel-echo pusher-js

然后在.env里配置Pusher信息(如果用官方Pusher服务,或者用Laravel WebSockets自建):

BROADCAST_DRIVER=pusher
PUSHER_APP_ID=你的APP_ID
PUSHER_APP_KEY=你的APP_KEY
PUSHER_APP_SECRET=你的APP_SECRET
PUSHER_APP_CLUSTER=你的集群(比如ap1)

2. 创建消息广播事件

生成一个事件类,用来广播新消息:

php artisan make:event NewMessage

编辑app/Events/NewMessage.php:

namespace App\Events;

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

class NewMessage implements ShouldBroadcast
{
    use Dispatchable, SerializesModels;

    public $message;

    public function __construct(Message $message)
    {
        $this->message = $message;
    }

    // 只让会话的参与者订阅这个私有频道
    public function broadcastOn()
    {
        return new PrivateChannel('conversation.'.$this->message->conversation_id);
    }

    // 自定义广播事件名称,方便前端监听
    public function broadcastAs()
    {
        return 'new-message';
    }
}

3. 配置频道授权

在routes/channels.php里添加授权逻辑,确保只有会话参与者能订阅频道:

use App\Models\Conversation;

Broadcast::channel('conversation.{conversationId}', function ($user, $conversationId) {
    $conversation = Conversation::findOrFail($conversationId);
    // 检查当前用户是会话的发起方还是接收方
    return $conversation->user_id === $user->id || $conversation->recipient_id === $user->id;
});

4. 发送消息时触发广播

修改你发送消息的控制器方法,保存消息后触发广播:

public function sendMessage(Request $request)
{
    $message = Message::create([
        'conversation_id' => $request->conversation_id,
        'sender_id' => auth()->id(),
        'recipient_id' => $request->recipient_id,
        'content' => $request->content,
        'is_read' => 0
    ]);

    // 只给其他用户广播,发送方自己已经通过Ajax更新了消息
    broadcast(new NewMessage($message))->toOthers();

    return response()->json(['success' => true, 'message' => $message]);
}

5. 前端监听广播事件

先在resources/js/bootstrap.js里配置Laravel Echo:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: import.meta.env.VITE_PUSHER_APP_KEY,
    cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    forceTLS: true
});

然后在聊天页面的JS里监听新消息:

const conversationId = {{ $conversation->id }};

// 订阅私有频道,监听新消息事件
window.Echo.private(`conversation.${conversationId}`)
    .listen('.new-message', (e) => {
        // 收到新消息,直接追加到容器
        const msgHtml = `<div class="message received">${e.message.content}</div>`;
        $('#message-container').append(msgHtml);

        // 自动滚动到底部
        $('#message-container').scrollTop($('#message-container')[0].scrollHeight);

        // 标记消息为已读
        markMessagesAsRead(conversationId);
    });

// 标记已读的函数和轮询方案里的一样
function markMessagesAsRead(conversationId) {
    $.ajax({
        url: `/messages/mark-as-read/${conversationId}`,
        method: 'POST',
        data: { _token: '{{ csrf_token() }}' },
        success: () => console.log('消息已标记为已读')
    });
}

一些额外建议

  • 轮询方案适合小型项目或者快速迭代,WebSocket适合追求体验的中大型项目;
  • 不管用哪种方案,都可以在用户打开聊天页面时,自动标记该会话的所有未读消息为已读;
  • 发送消息时,发送方可以直接把消息追加到容器,不用全量刷新,提升流畅度;
  • 如果不想用Pusher服务,可以用laravel/websockets包搭建自己的WebSocket服务器,成本更低。

内容的提问来源于stack exchange,提问作者Andreas Hunter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:47:14