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

Laravel 11+Livewire 3.5中wire:poll引发页面变白问题

问题:Livewire添加wire:poll后页面变白

使用Laravel 11 + Livewire 3.5开发,通过php artisan make:livewire TvDisplay创建组件。给需要更新的元素加上wire:poll后,页面会在2.5秒后完全变白,而非仅更新目标组件。移除wire:poll后页面刷新正常,添加后到轮询时间点就出现变白问题。

控制器代码

<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Ticket;
use Illuminate\Support\Facades\Log;

class TvDisplay extends Component
{
    public $calledTicket;
    public $upcomingTickets;

    protected $listeners = ['ticketUpdated' => 'updateTickets'];

    public function mount()
    {
        $this->updateTickets();
    }

    public function updateTickets()
    {   
        $this->calledTicket = Ticket::where('served', 1)->latest()->first();
        $this->upcomingTickets = Ticket::where('served', 0)->orderBy('created_at', 'asc')->take(10)->get();

    }

    public function render()
    {
        return view('livewire.tv-display');
    }
}

视图代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TV Display</title>
    <style>
        body {
            background: url('storage/img/background.png') no-repeat center center fixed;
            background-size: cover;
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            overflow: hidden; /* Remove a barra de rolagem */
        }

        .container {
            color: white;
            height: 100vh;
            display: flex;
            flex-direction: row;
        }

        .left-panel {
            width: 28%;
            height: 100%;
            display: flex;
            flex-direction: column;
            justify-content: flex-start;
            align-items: center;
            padding: 20px;
            text-align: center;
            margin-top: 90px; /* Ajuste conforme necessário */
        }

        .left-panel h1, .left-panel h2 {
            margin-top: 0;
        }

        .table-container {
            max-width: 100%;
            display: flex;
            flex-direction: column;
            align-items: center; /* Centraliza o conteúdo da tabela */
        }

        .video-panel {
            width: 66.67%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .video-panel video {
            width: 98%;
            height: auto;
            max-width: 100%;
        }

        table {
            width: 100%;
            margin-top: 20px;
            border-collapse: collapse;
            text-align: center; /* Centraliza o texto nas células */
        }

        th, td {
            padding: 10px;
            font-size: 2.1em; /* Aumentando o tamanho da fonte */
        }

        thead {
            background-color: #f2f2f279;
        }

        .called-ticket {
            width: 100%;
            height: 130px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            background-color:#f2f2f279; /* Retângulo branco com opacidade baixa */
            margin-bottom: 20px;
            font-size: 2.3em; /* Aumentando o tamanho da fonte */
            padding: 10px; /* Adicionando padding para melhor visualização */
        }

        .called-ticket h2 {
            margin: 0;
        }

        .called-ticket h3 {
            margin: 10px 0 0 0;
            font-size: 1.1em; /* Ajuste o tamanho da fonte conforme necessário */
        }
    </style>
</head>
<body>
<div class="container" >

    <!-- Divisão da Tabela e Texto -->
    <div class="left-panel">
        <div class="table-container">

            <h1>Senha chamada</h1>

            <div class="called-ticket" wire:poll>
                
                @if ($calledTicket)
                    
                    <h2>{{ $calledTicket->queue->name }} - {{ $calledTicket->sequential }}</h2>
                    @if ($calledTicket->user && $calledTicket->user->servicePost)
                        <h3>{{ $calledTicket->user->servicePost->name }}</h3>
                    @else
                        <h3>--</h3>
                    @endif
                @else
                    <h2>--</h2>
                    <h3>--</h3>
                @endif
            </div wire:poll>
            <table>
                <thead>
                    <tr>
                        <th>Fila</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach ($upcomingTickets as $ticket)
                        <tr>
                            <td >{{ $ticket->queue->name }} - {{ $ticket->sequential }}</td>
                        </tr>
                    @endforeach
                </tbody>
            </table>
            

        </div>
    </div>

    <!-- Divisão do Vídeo -->
    <div class="video-panel">
        <video autoplay muted loop>
            <source src="{{ asset('storage/tv/SPC.mp4') }}" type="video/mp4">
        </video>
    </div>

</div>
</body>
</html>

问题原因

  1. 视图结构不符合Livewire规范:当前组件视图包含完整的HTML文档结构(<html>、<head>、<body>),但Livewire组件视图应该仅为内容片段。当wire:poll触发更新时,Livewire会尝试替换整个组件内容,导致页面DOM结构混乱,最终出现页面变白。
  2. wire:poll语法错误:视图中错误地在闭合标签上重复写了wire:poll,正确写法是仅在起始标签添加该属性。

解决方案

1. 拆分视图结构

将完整的HTML骨架移到父视图,组件视图仅保留业务内容:

修改后的组件视图(livewire.tv-display)

<div class="container">
    <!-- Divisão da Tabela e Texto -->
    <div class="left-panel">
        <div class="table-container">
            <h1>Senha chamada</h1>
            <div class="called-ticket" wire:poll.5s>
                @if ($calledTicket)
                    <h2>{{ $calledTicket->queue->name }} - {{ $calledTicket->sequential }}</h2>
                    @if ($calledTicket->user && $calledTicket->user->servicePost)
                        <h3>{{ $calledTicket->user->servicePost->name }}</h3>
                    @else
                        <h3>--</h3>
                    @endif
                @else
                    <h2>--</h2>
                    <h3>--</h3>
                @endif
            </div>
            <table>
                <thead>
                    <tr>
                        <th>Fila</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach ($upcomingTickets as $ticket)
                        <tr>
                            <td>{{ $ticket->queue->name }} - {{ $ticket->sequential }}</td>
                        </tr>
                    @endforeach
                </tbody>
            </table>
        </div>
    </div>
    <!-- Divisão do Vídeo -->
    <div class="video-panel">
        <video autoplay muted loop>
            <source src="{{ asset('storage/tv/SPC.mp4') }}" type="video/mp4">
        </video>
    </div>
</div>

创建父视图(tv-display.blade.php)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TV Display</title>
    <style>
        /* 原视图中的CSS样式全部移到这里 */
        body {
            background: url('storage/img/background.png') no-repeat center center fixed;
            background-size: cover;
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }

        .container {
            color: white;
            height: 100vh;
            display: flex;
            flex-direction: row;
        }

        .left-panel {
            width: 28%;
            height: 100%;
            display: flex;
            flex-direction: column;
            justify-content: flex-start;
            align-items: center;
            padding: 20px;
            text-align: center;
            margin-top: 90px;
        }

        .left-panel h1, .left-panel h2 {
            margin-top: 0;
        }

        .table-container {
            max-width: 100%;
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        .video-panel {
            width: 66.67%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .video-panel video {
            width: 98%;
            height: auto;
            max-width: 100%;
        }

        table {
            width: 100%;
            margin-top: 20px;
            border-collapse: collapse;
            text-align: center;
        }

        th, td {
            padding: 10px;
            font-size: 2.1em;
        }

        thead {
            background-color: #f2f2f279;
        }

        .called-ticket {
            width: 100%;
            height: 130px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            background-color:#f2f2f279;
            margin-bottom: 20px;
            font-size: 2.3em;
            padding: 10px;
        }

        .called-ticket h2 {
            margin: 0;
        }

        .called-ticket h3 {
            margin: 10px 0 0 0;
            font-size: 1.1em;
        }
    </style>
    @livewireStyles
</head>
<body>
    @livewire('tv-display')
    @livewireScripts
</body>
</html>

2. 更新路由指向父视图

// routes/web.php
Route::get('/tv-display', function () {
    return view('tv-display');
});

3. 修正wire:poll语法

确保wire:poll仅添加在起始标签,可通过.5s指定轮询间隔(避免默认2.5秒过于频繁):

<div class="called-ticket" wire:poll.5s>
    <!-- 内容 -->
</div>

4. 优化数据查询(可选)

在控制器中使用预加载避免N+1查询:

public function updateTickets()
{   
    $this->calledTicket = Ticket::with(['queue', 'user.servicePost'])
        ->where('served', 1)
        ->latest()
        ->first();
    $this->upcomingTickets = Ticket::with('queue')
        ->where('served', 0)
        ->orderBy('created_at', 'asc')
        ->take(10)
        ->get();
}

内容的提问来源于stack exchange,提问作者Henrique Postalli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 13:57:02