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

在Laravel与Vue.js中实现WebSocket以实现实时数据更新

用WebSocket替代Laravel+Vue/Quasar应用的轮询方案

背景

我的应用后端基于Laravel,前端采用Vue.js和Quasar框架。原本Vue视图每5秒发送一次请求做轮询,现在已经完成Pusher与Laravel Echo的配置,想要用WebSocket替代轮询,实现监听数据表的所有更新,避免频繁请求和加载状态的频繁触发。

目前前端表格的分页逻辑由前端实现,通过请求后端获取数据展示;加载逻辑为发起请求时显示加载状态,请求完成后隐藏,轮询时每5秒会触发一次加载。

当前轮询代码片段

初始化轮询的init函数

async init() {
  this.onRequest({
    pagination: this.pagination,
    filter: undefined
  })
  this.timeout = setTimeout(this.init, 5000);
}

请求数据的onRequest函数

onRequest(props) {
  const { page, rowsPerPage } = props.pagination
  const { search } = this
  this.loading = true
  DataService.index({ params: { page, rowsPerPage, search } }).then((payrolls) => {
    this.data = payrolls.data
    this.pagination.rowsPerPage = payrolls.per_page
    this.pagination.page = payrolls.current_page
    this.pagination.rowsNumber = payrolls.total
    this.loading = false
  }).catch(() => {
    this.loading = false
  })
}

实现WebSocket监听数据表更新的步骤

1. 后端配置:触发数据表更新事件

在Laravel的模型中,利用模型事件(如created/updated/deleted)或者观察者,当数据表有更新时,通过Pusher发送事件。

示例:模型事件触发广播

比如在Payroll模型的boot方法中添加:

protected static function boot()
{
    parent::boot();

    // 监听创建、更新、删除事件
    static::created(function ($payroll) {
        broadcast(new PayrollUpdated($payroll, 'created'))->toOthers();
    });

    static::updated(function ($payroll) {
        broadcast(new PayrollUpdated($payroll, 'updated'))->toOthers();
    });

    static::deleted(function ($payroll) {
        broadcast(new PayrollUpdated($payroll, 'deleted'))->toOthers();
    });
}

创建广播事件

生成PayrollUpdated事件:

php artisan make:event PayrollUpdated

在事件类中实现ShouldBroadcast接口,并定义广播频道和数据:

namespace App\Events;

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

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

    public $payroll;
    public $action;

    /**
     * 创建事件实例
     */
    public function __construct(Payroll $payroll, string $action)
    {
        $this->payroll = $payroll;
        $this->action = $action;
    }

    /**
     * 指定事件广播的频道
     */
    public function broadcastOn(): array
    {
        // 根据需求选择公共频道或私有频道
        return [
            new Channel('payrolls'),
        ];
    }

    // 自定义广播数据(可选)
    public function broadcastWith(): array
    {
        return [
            'data' => $this->payroll->toArray(),
            'action' => $this->action,
            'timestamp' => now()->toDateTimeString()
        ];
    }
}

2. 前端配置:监听WebSocket事件并更新数据

在Vue组件中,通过Laravel Echo监听后端广播的PayrollUpdated事件,根据事件类型(创建/更新/删除)更新前端表格数据,无需重新发起请求。

步骤1:移除轮询逻辑

删除init函数中的定时器,停止轮询:

// 移除定时器
clearTimeout(this.timeout);
// 不再调用setTimeout(this.init, 5000)

步骤2:监听WebSocket事件

在组件的mounted钩子中初始化Laravel Echo监听:

mounted() {
  // 首次加载数据(保留原有分页逻辑)
  this.onRequest({
    pagination: this.pagination,
    filter: undefined
  });

  // 监听payrolls频道的PayrollUpdated事件
  window.Echo.channel('payrolls')
    .listen('PayrollUpdated', (e) => {
      const { data, action } = e;
      switch(action) {
        case 'created':
          this.handleCreated(data);
          break;
        case 'updated':
          this.handleUpdated(data);
          break;
        case 'deleted':
          this.handleDeleted(data);
          break;
      }
    });
},

beforeUnmount() {
  // 组件销毁时取消监听
  window.Echo.channel('payrolls').stopListening('PayrollUpdated');
  clearTimeout(this.timeout);
}

步骤3:实现数据更新逻辑

根据事件类型更新前端数据,同时处理分页统计:

methods: {
  // 处理新增数据
  handleCreated(newPayroll) {
    // 如果当前页是第一页且数据未达每页条数,直接添加到列表
    if (this.pagination.page === 1 && this.data.length < this.pagination.rowsPerPage) {
      this.data.unshift(newPayroll);
    }
    // 更新总条数
    this.pagination.rowsNumber += 1;
  },

  // 处理更新数据
  handleUpdated(updatedPayroll) {
    // 找到对应数据并替换
    const index = this.data.findIndex(item => item.id === updatedPayroll.id);
    if (index !== -1) {
      this.data.splice(index, 1, updatedPayroll);
    }
  },

  // 处理删除数据
  handleDeleted(deletedPayroll) {
    // 移除对应数据
    this.data = this.data.filter(item => item.id !== deletedPayroll.id);
    // 更新总条数
    this.pagination.rowsNumber -= 1;

    // 如果当前页数据为空且不是第一页,自动跳转到上一页
    if (this.data.length === 0 && this.pagination.page > 1) {
      this.pagination.page -= 1;
      this.onRequest({ pagination: this.pagination, filter: undefined });
    }
  },

  // 保留原有的onRequest函数用于分页和搜索
  onRequest(props) {
    const { page, rowsPerPage } = props.pagination
    const { search } = this
    this.loading = true
    DataService.index({ params: { page, rowsPerPage, search } }).then((payrolls) => {
      this.data = payrolls.data
      this.pagination.rowsPerPage = payrolls.per_page
      this.pagination.page = payrolls.current_page
      this.pagination.rowsNumber = payrolls.total
      this.loading = false
    }).catch(() => {
      this.loading = false
    })
  }
}

3. 注意事项

  • 频道权限:如果需要私有频道(比如只允许特定用户查看数据),可以使用PrivateChannel,并在routes/channels.php中定义授权逻辑。
  • 数据一致性:多用户同时操作时,WebSocket事件能保证数据实时同步,需注意处理并发场景(如同一数据被多次更新)。
  • 首次加载:保留onRequest的首次调用用于加载初始分页数据,后续数据更新通过WebSocket事件处理,无需轮询。
  • 错误处理:可给Echo添加错误监听,处理WebSocket连接断开的情况,比如自动重连或 fallback 到轮询(可选)。

内容的提问来源于stack exchange,提问作者Orlando Correa Jiménez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 21:53:11