Перейти к содержанию

Ленивая загрузка⚓︎

Livewire позволяет лениво загружать компоненты, которые в противном случае замедляли бы начальную загрузку страницы.

Сравнение ленивой и отложенной загрузки⚓︎

Livewire предоставляет два способа отложить загрузку компонентов:

  • Ленивая загрузка (lazy): Компоненты загружаются, когда они становятся видимыми в области просмотра (когда пользователь прокручивает страницу до них)
  • Отложенная загрузка (defer): Компоненты загружаются сразу после завершения начальной загрузки страницы

Оба подхода предотвращают блокировку начального рендеринга страницы медленными компонентами, но отличаются моментом, когда компонент фактически загружается.

Простой пример⚓︎

Например, представьте, что у вас есть компонент revenue, который содержит тяжёлый запрос к базе данных в методе mount():

resources/views/components/⚡revenue.blade.php
<?php

use Livewire\Component;
use App\Models\Transaction;

new class extends Component {
    public $amount;

    public function mount()
    {
        // Тяжёлый запрос к базе данных...
        $this->amount = Transaction::monthToDate()->sum('amount');
    }
};
?>

<div>
    Доход за этот месяц: {{ $amount }}
</div>

Без ленивой загрузки этот компонент задерживал бы загрузку всей страницы и создавал бы ощущение медленной работы всего приложения.

Чтобы включить ленивую загрузку, можно передать параметр lazy в компонент:

<livewire:revenue lazy />

Теперь, вместо того чтобы загружать компонент сразу, Livewire пропустит этот компонент, загрузив страницу без него. Затем, когда компонент станет видимым в области просмотра, Livewire выполнит сетевой запрос, чтобы полностью загрузить этот компонент на страницу.

Запросы lazy и defer по умолчанию изолированы

В отличие от других сетевых запросов в Livewire, обновления ленивых (lazy) и отложенных (deferred) компонентов изолированы друг от друга при отправке на сервер. Это позволяет сохранять высокую скорость загрузки, загружая каждый компонент параллельно. Подробнее о группировке компонентов →

Отрисовка placeholder HTML⚓︎

По умолчанию Livewire вставит пустой <div></div> вместо вашего компонента до тех пор, пока он полностью не загрузится. Поскольку изначально компонент невидим для пользователей, его внезапное появление на странице может выглядеть резко и неприятно.

Чтобы сообщить пользователям, что компонент загружается, можно отобразить placeholder-контент — например, спиннеры загрузки или скелетные (skeleton) заглушки.

Использование директивы @placeholder⚓︎

Для однофайловых и многофайловых компонентов вы можете использовать директиву @placeholder прямо в вашем шаблоне, чтобы указать содержимое заглушки:

resources/views/components/⚡revenue.blade.php
<?php

use Livewire\Component;
use App\Models\Transaction;

new class extends Component {
    public $amount;

    public function mount()
    {
        // Тяжёлый запрос к базе данных...
        $this->amount = Transaction::monthToDate()->sum('amount');
    }
};
?>

@placeholder
    <div>
        <!-- Загрузка спиннера... -->
        <svg>...</svg>
    </div>
@endplaceholder

<div>
    Доход за этот месяц: {{ $amount }}
</div>

Содержимое между @placeholder и @endplaceholder будет отображаться, пока компонент загружается, а затем будет заменено настоящим содержимым компонента после его загрузки.

Директива placeholder только для компонентов на основе шаблонов

Директива @placeholder доступна только для компонентов на основе представления — однофайловых и многофайловых. Для компонентов на основе классов вместо неё используйте метод placeholder().

Placeholder и компонент должны использовать один и тот же тип корневого элемента

Например, если корневой элемент placeholder — это div, то и сам компонент тоже должен использовать элемент div.

Использование метода placeholder()⚓︎

Для компонентов на основе классов, или если вам нужен программный контроль, вы можете определить метод placeholder(), который возвращает HTML:

<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Transaction;

class Revenue extends Component
{
    public $amount;

    public function mount()
    {
        // Тяжёлый запрос к базе данных...
        $this->amount = Transaction::monthToDate()->sum('amount');
    }

    public function placeholder()
    {
        return <<<'HTML'
        <div>
            <!-- Загрузка спиннера... -->
            <svg>...</svg>
        </div>
        HTML;
    }

    public function render()
    {
        return view('livewire.revenue');
    }
}

Для более сложных индикаторов загрузки (например, скелетонов) вы можете возвращать view из метода placeholder():

<?php

public function placeholder(array $params = [])
{
    return view('livewire.placeholders.skeleton', $params);
}

Любые параметры, переданные в компонент с ленивой загрузкой, будут доступны в виде аргумента $params в методе placeholder().

Загрузка сразу после загрузки страницы⚓︎

По умолчанию компоненты с ленивой загрузкой (lazy) полностью загружаются только тогда, когда попадают в область видимости браузера, например, когда пользователь прокручивает страницу до них.

Если же вы хотите, чтобы компонент загружался сразу после завершения загрузки страницы, не дожидаясь попадания в область видимости, вместо этого можно использовать параметр defer:

<livewire:revenue defer />

Теперь этот компонент будет загружаться сразу после готовности страницы, без ожидания, пока он станет видимым в области просмотра.

Вы также можете использовать атрибут #[Defer], чтобы сделать компонент отложенно загружаемым по умолчанию:

<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\Attributes\Defer;

#[Defer]
class Revenue extends Component
{
    // ...
}

Устаревший синтаксис on-load

Вы также можете использовать lazy="on-load", который ведёт себя точно так же, как defer. Для нового кода рекомендуется использовать параметр defer.

Передача пропсов⚓︎

В общем случае с компонентами lazy можно работать так же, как с обычными компонентами, поскольку данные извне в них всё равно можно передавать.

Например, вот ситуация, когда вы можете передать временной интервал в компонент Revenue из родительского компонента:

<input type="date" wire:model="start">
<input type="date" wire:model="end">

<livewire:revenue lazy :$start :$end />

Вы можете принимать эти данные в методе mount() точно так же, как и в любом другом компоненте:

resources/views/components/⚡revenue.blade.php
<?php

use Livewire\Component;
use App\Models\Transaction;

new class extends Component {
    public $amount;

    public function mount($start, $end)
    {
        // Тяжёлый запрос к базе данных...
        $this->amount = Transactions::between($start, $end)->sum('amount');
    }
};
?>

@placeholder
    <div>
        <!-- Загрузка спиннера... -->
        <svg>...</svg>
    </div>
@endplaceholder

<div>
    Доход за этот месяц: {{ $amount }}
</div>

Однако, в отличие от обычной загрузки компонента, компонент с атрибутом lazy должен сериализовать (или «дегидратировать») все переданные ему свойства и временно сохранить их на стороне клиента до тех пор, пока компонент полностью не загрузится.

Например, вы можете захотеть передать в компонент revenue модель Eloquent следующим образом:

<livewire:revenue lazy :$user />

В обычном компоненте непосредственно в памяти PHP-модель $user передавалась бы в метод mount() компонента revenue. Однако поскольку метод mount() не будет выполнен до следующего сетевого запроса, Livewire внутренне сериализует $user в JSON, а затем повторно запросит его из базы данных перед обработкой следующего запроса.

Как правило, эта сериализация не должна вызывать никаких поведенческих различий в вашем приложении.

Принудительная ленивая или отложенная загрузка по умолчанию⚓︎

Если вы хотите гарантировать, что все использования компонента будут лениво загружаться или откладываться, вы можете добавить атрибут #[Lazy] или #[Defer] над классом компонента:

<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\Attributes\Lazy;

#[Lazy]
class Revenue extends Component
{
    // ...
}

Или для отложенной загрузки:

<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\Attributes\Defer;

#[Defer]
class Revenue extends Component
{
    // ...
}

Вы можете переопределять эти настройки по умолчанию при отрисовке компонента:

<?php

{czjqqkd:0 Отключаем ленивую загрузку czjqqkd:1}
<livewire:revenue :lazy="false" />

{czjqqkd:2 Отключаем отложенную загрузку czjqqkd:3}
<livewire:revenue :defer="false" />

Группировка нескольких ленивых компонентов⚓︎

По умолчанию, если на странице находится несколько компонентов с ленивой загрузкой (lazy), каждый из них выполняет независимый сетевой запрос параллельно с остальными. Это часто является желательным поведением с точки зрения производительности, так как каждый компонент загружается самостоятельно.

Однако, если на странице очень много ленивых компонентов, может быть полезно сгруппировать их в один сетевой запрос, чтобы снизить нагрузку на сервер.

Использование параметра bundle⚓︎

Вы можете включить группировку, передав параметр bundle: true:

<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\Attributes\Lazy;

#[Lazy(bundle: true)]
class Revenue extends Component
{
    // ...
}

Теперь, если на одной странице находится десять компонентов Revenue, при загрузке страницы все десять обновлений будут сгруппированы и отправлены на сервер в виде единого сетевого запроса.

Использование модификатора bundle⚓︎

Вы также можете включить группировку прямо при отрисовке компонента, используя модификатор bundle:

<livewire:revenue lazy.bundle />

Это также работает с отложенными компонентами:

<livewire:revenue defer.bundle />

Или с использованием атрибута:

<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\Attributes\Defer;

#[Defer(bundle: true)]
class Revenue extends Component
{
    // ...
}

Когда использовать группировку⚓︎

Используйте группировку, когда:

  • На одной странице находится много (5 и более) ленивых или отложенных компонентов
  • Компоненты схожи по сложности и времени загрузки
  • Вы хотите снизить нагрузку на сервер и количество HTTP-соединений

Не используйте группировку, когда:

  • Компоненты имеют сильно различающееся время загрузки (медленные компоненты будут блокировать быстрые)
  • Вы хотите, чтобы компоненты появлялись сразу после их индивидуальной готовности
  • На странице всего несколько ленивых компонентов

Устаревший синтаксис isolate

Вы также можете использовать isolate: false, который ведёт себя точно так же, как bundle: true. Для нового кода рекомендуется использовать параметр bundle, так как он более явно выражает намерение.

Ленивая загрузка полноразмерных страниц⚓︎

Вы можете применять ленивую загрузку или отложенную загрузку к полноразмерным Livewire-компонентам с помощью методов маршрутизации.

Ленивая загрузка полноразмерных страниц⚓︎

Используйте ->lazy(), чтобы компонент загружался, когда он попадает в область просмотра:

<?php

Route::livewire('/dashboard', 'pages::dashboard')->lazy();

Отложенная загрузка полноразмерных страниц⚓︎

Используйте ->defer(), чтобы компонент загружался сразу после загрузки страницы:

<?php

Route::livewire('/dashboard', 'pages::dashboard')->defer();

Отключение ленивой/отложенной загрузки⚓︎

Если компонент по умолчанию использует ленивую или отложенную загрузку (за счёт атрибута #[Lazy] или #[Defer]), вы можете отключить это поведение, передав enabled: false:

<?php

Route::livewire('/dashboard', 'pages::dashboard')->lazy(enabled: false);
Route::livewire('/dashboard', 'pages::dashboard')->defer(enabled: false);

Представление заглушки по умолчанию⚓︎

Если вы хотите установить представление заглушки по умолчанию для всех ваших компонентов, это можно сделать, указав нужный view в конфигурационном файле /config/livewire.php:

<?php

'component_placeholder' => 'livewire.placeholder',

Теперь, когда компонент загружается лениво и метод placeholder() не определён, Livewire будет использовать настроенное Blade-представление (в данном случае livewire.placeholder).

Отключение ленивой загрузки в тестах⚓︎

При юнит-тестировании ленивого компонента или страницы с вложенными ленивыми компонентами иногда требуется отключить поведение «lazy», чтобы иметь возможность проверять окончательный отрендеренный результат. В противном случае в тестах эти компоненты будут отображаться как их заглушки.

Вы можете легко отключить ленивую загрузку с помощью вспомогательного метода Livewire::withoutLazyLoading() в тестах следующим образом:

<?php

namespace Tests\Feature\Livewire;

use App\Livewire\Dashboard;
use Livewire\Livewire;
use Tests\TestCase;

class DashboardTest extends TestCase
{
    public function test_renders_successfully()
    {
        Livewire::withoutLazyLoading()
            ->test(Dashboard::class)
            ->assertSee(...);
    }
}

Теперь, когда компонент dashboard будет отрендерен в этом тесте, он пропустит отрисовку метода placeholder() и вместо этого отрендерит полный компонент так, как будто ленивая загрузка вообще не применялась.

Смотрите также⚓︎

  • Островки — Изолировать обновления внутри одного компонента
  • Состояния загрузки — Показывать заглушки, пока компоненты загружаются
  • @placeholder — Определять содержимое заглушки
  • Атрибут Lazy — Помечать компоненты для ленивой загрузки