Вложенные компоненты⚓︎
Livewire позволяет вкладывать дополнительные компоненты Livewire внутрь родительского компонента. Эта возможность чрезвычайно мощная, поскольку позволяет повторно использовать и инкапсулировать поведение в компонентах Livewire, которые используются по всему вашему приложению.
Возможно, вам не нужен компонент Livewire
Прежде чем выносить часть шаблона в отдельный вложенный компонент Livewire, задайте себе вопрос: Должен ли этот участок быть «живым»? Если нет, мы рекомендуем создать обычный Blade-компонент. Создавайте компонент Livewire только в том случае, если он выигрывает от динамической природы Livewire или если есть явная выгода в производительности.
Рассмотрите островки для изолированных обновлений
Если вы хотите изолировать повторный рендеринг в определённых областях компонента без накладных расходов на создание отдельных дочерних компонентов, лучше использовать островки. Островки позволяют создавать независимо обновляемые регионы внутри одного компонента без необходимости управлять пропсами, событиями или коммуникацией между дочерними компонентами.
Подробное техническое объяснение вложенности компонентов Livewire, включая вопросы производительности, последствия использования и ограничения, смотрите в глубоком разборе вложенности компонентов.
Вложение компонента⚓︎
Чтобы вложить компонент Livewire в родительский, просто включите его в Blade-представление родительского компонента. Ниже приведён пример родительского компонента dashboard, который содержит вложенный компонент todos:
<?php
use Livewire\Component;
new class extends Component {
//
};
?>
<div>
<h1>Дашборд</h1>
<livewire:todos />
</div>
При первоначальной отрисовке этой страницы компонент dashboard встретит <livewire:todos /> и отрендерит его на своём месте. При последующих сетевых запросах к dashboard вложенный компонент todos пропустит этап отрисовки, потому что теперь он является самостоятельным независимым компонентом на странице. Подробнее о технических концепциях вложенности и отрисовки можно прочитать в нашей документации по теме, почему вложенные компоненты независимы.
Подробнее о синтаксисе отрисовки компонентов можно узнать в разделе Отрисовка компонентов.
Передача пропсов дочерним компонентам⚓︎
Передача данных из родительского компонента в дочерний — довольно простая задача. По сути, это очень похоже на передачу пропсов обычному Blade-компоненту.
Например, рассмотрим компонент todos, который передаёт коллекцию $todos дочернему компоненту с именем todo-count:
<?php
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Computed;
use Livewire\Component;
new class extends Component {
#[Computed]
public function todos()
{
return Auth::user()->todos,
}
};
?>
<div>
<livewire:todo-count :todos="$this->todos" />
<!-- ... -->
</div>
Как вы можете видеть, мы передаём $this->todos в компонент todo-count с помощью синтаксиса: :todos="$this->todos".
Теперь, когда $todos передан в дочерний компонент, вы можете получить эти данные через метод mount() дочернего компонента:
<?php
use Livewire\Attributes\Computed;
use Livewire\Component;
use App\Models\Todo;
new class extends Component {
public $todos;
public function mount($todos)
{
$this->todos = $todos;
}
#[Computed]
public function count()
{
return $this->todos->count(),
}
};
?>
<div>
Count: {{ $this->count }}
</div>
Пропуск mount() как более короткая альтернатива
Если метод mount() в приведённом выше примере кажется вам избыточным шаблонным кодом, его можно опустить — при условии, что имена свойства и параметра совпадают:
Передача статических пропсов⚓︎
В предыдущем примере мы передавали пропсы в дочерний компонент с использованием динамического синтаксиса Livewire, который поддерживает выражения PHP следующим образом:
Однако иногда вам может понадобиться передать компоненту простое статическое значение, например строку. В таких случаях вы можете опустить двоеточие в начале выражения:
Булевы значения можно передавать компонентам, просто указывая ключ без значения. Например, чтобы передать переменную $inline со значением true в компонент, достаточно просто написать inline в теге компонента:
Упрощённый синтаксис атрибутов⚓︎
Когда вы передаёте PHP-переменные в компонент, имя переменной и имя пропа часто совпадают. Чтобы не писать одно и то же имя дважды, Livewire позволяет просто поставить двоеточие перед именем переменной:
Рендеринг дочерних элементов в цикле⚓︎
При рендеринге дочернего компонента внутри цикла следует указывать уникальное значение key для каждой итерации.
Ключи компонентов позволяют Livewire отслеживать каждый компонент при последующих отрисовках, особенно если компонент уже был отрисован или если несколько компонентов поменяли порядок на странице.
Вы можете указать ключ компонента, задав проп :key на дочернем компоненте:
<div>
<h1>Список дел</h1>
@foreach ($todos as $todo)
<livewire:todo-item :$todo :wire:key="$todo->id" />
@endforeach
</div>
Как видите, каждый дочерний компонент получит уникальный ключ, установленный в ID каждого $todo. Это гарантирует, что ключ будет уникальным и будет отслеживаться, если todos будут переупорядочены.
Ключи не опциональны
Если вы использовали фронтенд-фреймворки вроде Vue или Alpine, вы знакомы с добавлением ключа к вложенному элементу в цикле. Однако в этих фреймворках ключ не обязателен, то есть элементы отрендерятся, но при переупорядочивании отслеживание может работать некорректно. Однако Livewire полагается на ключи гораздо сильнее и будет вести себя неправильно без них.
Реактивные пропсы⚓︎
Разработчики, новые в Livewire, ожидают, что пропсы "реактивны" по умолчанию. Другими словами, они ожидают, что когда родитель изменяет значение пропса, переданного дочернему компоненту, дочерний компонент автоматически обновится. Однако по умолчанию пропсы Livewire не реактивны.
При использовании Livewire каждый компонент независим. Это означает, что когда обновление инициируется на родителе и отправляется сетевой запрос, на сервер отправляется только состояние родительского компонента для повторного рендеринга — не дочернего. Цель такого поведения — передавать между сервером и клиентом минимально возможный объём данных, делая обновления максимально производительными.
Но если вам нужно, чтобы пропс был реактивным, вы можете легко включить это поведение с помощью параметра атрибута #[Reactive].
Например, ниже приведён шаблон родительского компонента todos. Внутри него рендерится компонент todo-count, которому передаётся текущий список todos:
Теперь давайте добавим атрибут #[Reactive] к свойству $todos в компоненте todo-count. После этого любые добавления или удаления элементов в массиве $todos внутри родительского компонента будут автоматически вызывать обновление в компоненте todo-count:
<?php
use Livewire\Attributes\Reactive;
use Livewire\Attributes\Computed;
use Livewire\Component;
use App\Models\Todo;
new class extends Component {
#[Reactive]
public $todos;
#[Computed]
public function count()
{
return $this->todos->count(),
}
};
?>
<div>
Количество: {{ $this->count }}
</div>
Реактивные свойства — это невероятно мощная возможность, которая делает Livewire гораздо ближе к фронтенд-библиотекам компонентов, таким как Vue и React. Однако очень важно понимать последствия для производительности, которые несёт эта функция, и добавлять атрибут #[Reactive] только тогда, когда это действительно оправдано в вашем конкретном случае.
Островки могут устранить необходимость в реактивных пропсах
Если вы замечаете, что создаёте дочерние компоненты в основном для изоляции обновлений и используете #[Reactive] только для поддержания синхронизации, подумайте о применении островков. Островки позволяют изолированно перерисовывать части внутри одного компонента без необходимости в реактивных свойствах и обмене данными между родительским и дочерним компонентами.
Привязка к данным дочернего компонента с помощью wire:model⚓︎
Ещё один очень мощный паттерн обмена состоянием между родительским и дочерним компонентами — это использование директивы wire:model непосредственно на дочернем компоненте благодаря возможности Livewire под названием Modelable.
Такое поведение часто требуется, когда вы выносите поле ввода (input) в отдельный самостоятельный Livewire-компонент, но при этом хотите продолжать работать с его значением из родительского компонента.
Ниже приведён пример родительского компонента todos, который содержит свойство $todo, отслеживающее текущую задачу, которую пользователь собирается добавить:
<?php
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Computed;
use Livewire\Component;
use App\Models\Todo;
new class extends Component {
public $todo = '';
public function add()
{
Todo::create([
'content' => $this->pull('todo'),
]);
}
#[Computed]
public function todos()
{
return Auth::user()->todos,
}
};
Как вы можете видеть в шаблоне компонента todos, директива wire:model используется для прямой привязки свойства $todo к вложенному компоненту todo-input:
<div>
<h1>Список дел</h1>
<livewire:todo-input wire:model="todo" />
<button wire:click="add">Добавить пункт</button>
<div>
@foreach ($this->todos as $todo)
<livewire:todo-item :$todo :wire:key="$todo->id" />
@endforeach
</div>
</div>
Livewire предоставляет атрибут #[Modelable], который вы можете добавить к любому свойству дочернего компонента, чтобы сделать его modelable (привязываемым через wire:model) из родительского компонента.
Ниже приведён компонент todo-input с добавленным атрибутом #[Modelable] над свойством $value, чтобы сообщить Livewire: если родительский компонент использует wire:model на этом компоненте, то привязка должна происходить именно к этому свойству:
<?php
use Livewire\Attributes\Modelable;
use Livewire\Component;
new class extends Component {
#[Modelable]
public $value = '';
};
?>
<div>
<input type="text" wire:model="value" >
</div>
Теперь родительский компонент todos может работать с todo-input как с любым другим элементом ввода и напрямую привязываться к его значению, используя wire:model.
Предупреждение
В настоящее время Livewire поддерживает только один атрибут #[Modelable], поэтому будет привязан только первый из них.
Слоты⚓︎
Слоты позволяют передавать контент Blade из родительского компонента в дочерний. Это полезно, когда дочернему компоненту нужно отрисовать свой собственный контент, но при этом родительский компонент должен иметь возможность вставить свой контент в определенные места.
Ниже приведен пример родительского компонента, который отображает список комментариев. Каждый комментарий рендерится дочерним компонентом Comment, но родитель передает кнопку "Удалить" через слот:
<?php
use Livewire\Attributes\Computed;
use Livewire\Component;
use App\Models\Post;
new class extends Component {
public Post $post;
#[Computed]
public function comments()
{
return $this->post->comments;
}
public function removeComment($id)
{
$this->post->comments()->find($id)->delete();
}
};
?>
<div>
@foreach ($this->comments as $comment)
<livewire:comment :$comment :wire:key="$comment->id">
<button wire:click="removeComment({{ $comment->id }})">
Удалить
</button>
</livewire:comment>
@endforeach
</div>
Теперь, когда содержимое было передано в дочерний компонент Comment, вы можете отрендерить его с помощью переменной $slot:
<?php
use Livewire\Component;
use App\Models\Comment;
new class extends Component {
public Comment $comment;
};
?>
<div>
<p>{{ $comment->author }}</p>
<p>{{ $comment->body }}</p>
{{ $slot }}
</div>
Когда компонент Comment отрисовывает $slot, Livewire подставит туда содержимое, переданное из родительского компонента.
Важно понимать, что слоты оцениваются в контексте родительского компонента. Это означает, что любые свойства или методы, на которые есть ссылки внутри слота, принадлежат родителю, а не дочернему компоненту. В приведённом выше примере метод removeComment() вызывается на родительском компоненте, а не на дочернем Comment.
Именованные слоты⚓︎
Помимо слота по умолчанию, вы также можете передавать в дочерний компонент несколько именованных слотов. Это удобно, когда нужно предоставить содержимое для разных частей дочернего компонента.
Ниже приведён пример передачи одновременно слота по умолчанию и именованного слота actions в компонент Comment:
<div>
@foreach ($this->comments as $comment)
<livewire:comment :$comment :wire:key="$comment->id">
<livewire:slot name="actions">
<button wire:click="removeComment({{ $comment->id }})">
Remove
</button>
</livewire:slot>
<span>Posted on {{ $comment->created_at }}</span>
</livewire:comment>
@endforeach
</div>
Вы можете получить доступ к именованным слотам в дочернем компоненте, передав имя слота в переменную $slots:
<div>
<p>{{ $comment->author }}</p>
<p>{{ $comment->body }}</p>
<div class="actions">
{{ $slots['actions'] }}
</div>
<div class="metadata">
{{ $slot }}
</div>
</div>
Проверка передачи слота⚓︎
Вы можете проверить, был ли слот передан родительским компонентом, с помощью метода has() на переменной $slots. Это удобно, когда вы хотите условно отображать содержимое в зависимости от того, присутствует слот или нет:
<div>
<p>{{ $comment->author }}</p>
<p>{{ $comment->body }}</p>
@if ($slots->has('actions'))
<div class="actions">
{{ $slots['actions'] }}
</div>
@endif
{{ $slot }}
</div>
Прокидывание HTML-атрибутов⚓︎
Как и Blade-компоненты, компоненты Livewire поддерживают передачу (прокидывание) HTML-атрибутов от родительского компонента к дочернему с помощью переменной $attributes.
Ниже приведён пример родительского компонента, который передаёт атрибут class дочернему компоненту:
Вы можете применить эти атрибуты в дочернем компоненте с помощью переменной $attributes:
<div {{ $attributes->class('bg-white rounded-md') }}>
<p>{{ $comment->author }}</p>
<p>{{ $comment->body }}</p>
</div>
Атрибуты, имена которых совпадают с именами публичных свойств компонента, автоматически передаются как пропсы и исключаются из $attributes. Все оставшиеся атрибуты (например, class, id или data-*) остаются доступны через переменную $attributes.
Островки в сравнении с вложенными компонентами⚓︎
При разработке приложений на Livewire вы часто сталкиваетесь с выбором: создавать вложенный дочерний компонент или использовать островок? Оба подхода позволяют изолировать обновления в определённых областях страницы, но они решают разные задачи.
Когда использовать островки⚓︎
Островки идеально подходят, когда вам нужна изоляция производительности без лишней архитектурной сложности. Используйте островки в следующих случаях:
Вам нужна оптимизация производительности без лишних накладных расходов
Если ваша главная цель — предотвратить ненужное выполнение дорогостоящих вычислений, островки являются более простым и лёгким решением:
{ Островок: Простая изоляция производительности }
@island
<div>
Доход: {{ $this->expensiveRevenue }}
<button wire:click="$refresh">Обновить</button>
</div>
@endisland
Это даёт тот же выигрыш в производительности, что и вложенный дочерний компонент, но без необходимости создавать отдельный файл компонента, управлять пропсами или настраивать обмен событиями.
Вы хотите отложить (defer) или лениво загрузить (lazy load) содержимое
Островки отлично подходят для отложенного выполнения дорогостоящих операций после начальной загрузки страницы:
У вас несколько независимых областей интерфейса
Когда у вас есть несколько регионов, которые обновляются независимо друг от друга, но им не нужна отдельная сложная логика:
@island(name: 'stats')
<div>Stats: {{ $this->stats }}</div>
@endisland
@island(name: 'chart')
<div>Chart: {{ $this->chartData }}</div>
@endisland
Изолированная область не нуждается в собственном жизненном цикле
Островки полностью разделяют жизненный цикл, состояние и методы родительского компонента. Это идеальный вариант, когда изолируемая область концептуально является частью того же самого компонента.
Когда использовать вложенные компоненты⚓︎
Вложенные компоненты лучше подходят, когда вам нужна настоящая инкапсуляция и повторное использование. Используйте вложенные компоненты в следующих случаях:
Вам нужна повторно используемая, самодостаточная функциональность
Если компонент будет использоваться в нескольких местах приложения и должен иметь собственную логику и состояние:
{ Этот todo-item можно повторно использовать по всему приложению }
<livewire:todo-item :$todo :wire:key="$todo->id" />
Вам нужны отдельные хуки жизненного цикла
Когда дочернему компоненту нужны собственные методы mount(), updated() или другие хуки жизненного цикла:
<?php
public function mount($todo)
{
$this->authorize('view', $todo);
}
public function updated($property)
{
// Логика обновления, специфичная для этого дочернего компонента
}
Вам нужна инкапсулированная состояние и логика
Когда у дочернего компонента есть сложное управление состоянием, которое должно быть изолировано:
<?php
// Дочерний компонент со своим собственным инкапсулированным состоянием
public $editMode = false;
public $draft = '';
public function startEdit() { /* ... */ }
public function saveEdit() { /* ... */ }
public function cancelEdit() { /* ... */ }
Вам нужен по-настоящему независимый компонент
Вложенные компоненты действительно независимы, они сохраняют своё собственное состояние при обновлениях родителя. Это ценно, когда вы не хотите, чтобы перерисовки родителя влияли на дочерний компонент.
Вы создаёте библиотеку компонентов
Когда вы создаёте переиспользуемые компоненты для своей команды или организации, вложенные компоненты обеспечивают правильные границы инкапсуляции.
Краткое руководство по выбору⚓︎
Всё ещё не уверены? Задайте себе вопросы:
- «Нужно ли это переиспользовать в разных местах?» → Вложенный компонент
- «Нужны ли собственные методы жизненного цикла?» → Вложенный компонент
- «Я просто пытаюсь оптимизировать производительность?» → Островок
- «Хочу ли я отложить загрузку тяжёлого контента?» → Островок (с
lazyилиdefer) - «Это будет использоваться только в одном месте?» → Скорее всего островок
- «Нужна ли сложная, изолированная логика состояния?» → Вложенный компонент
Запомните: всегда можно начать с островка ради простоты, а позже переделать во вложенный компонент, если понадобится дополнительная инкапсуляция.
Прослушивание событий от дочерних компонентов⚓︎
Ещё одна мощная техника общения между родительским и дочерним компонентами — это система событий Livewire, которая позволяет отправлять событие на сервере или клиенте, а другие компоненты могут его перехватывать.
Наша полная документация по системе событий Livewire содержит более подробную информацию о событиях, но ниже мы рассмотрим простой пример использования события для запуска обновления в родительском компоненте.
Представьте компонент todos с функционалом отображения и удаления задач:
<?php
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Computed;
use Livewire\Component;
use App\Models\Todo;
new class extends Component {
public function remove($todoId)
{
$todo = Todo::find($todoId);
$this->authorize('delete', $todo);
$todo->delete();
}
#[Computed]
public function todos()
{
return Auth::user()->todos,
}
};
?>
<div>
@foreach ($this->todos as $todo)
<livewire:todo-item :$todo :wire:key="$todo->id" />
@endforeach
</div>
Чтобы вызвать метод remove() изнутри дочерних компонентов todo-item, вы можете добавить прослушиватель событий в компонент todos с помощью атрибута #[On]:
<?php
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;
use App\Models\Todo;
new class extends Component {
#[On('remove-todo')]
public function remove($todoId)
{
$todo = Todo::find($todoId);
$this->authorize('delete', $todo);
$todo->delete();
}
#[Computed]
public function todos()
{
return Auth::user()->todos,
}
};
?>
<div>
@foreach ($this->todos as $todo)
<livewire:todo-item :$todo :wire:key="$todo->id" />
@endforeach
</div>
После того как атрибут добавлен к методу, вы можете отправить событие remove-todo из дочернего компонента todo-item:
<?php
use Livewire\Component;
use App\Models\Todo;
new class extends Component {
public Todo $todo;
public function remove()
{
$this->dispatch('remove-todo', todoId: $this->todo->id);
}
};
?>
<div>
<span>{{ $todo->content }}</span>
<button wire:click="remove">Удалить</button>
</div>
Теперь, когда кнопка «Удалить» нажата внутри компонента todo-item, родительский компонент todos перехватит отправленное событие и выполнит удаление задачи.
После того как задача удалена в родительском компоненте, список будет перерисован, и дочерний компонент, отправивший событие remove-todo, исчезнет со страницы.
Улучшение производительности путём отправки события на стороне клиента⚓︎
Хотя приведённый выше пример работает, для выполнения одного действия требуется два сетевых запроса:
- Первый сетевой запрос от компонента
todo-itemзапускает действиеremove, которое отправляет событиеremove-todo. - Второй сетевой запрос происходит после того, как событие
remove-todoотправлено на стороне клиента и перехвачено компонентомtodosдля вызова его методаremove.
Вы можете полностью избежать первого запроса, отправляя событие remove-todo непосредственно на стороне клиента. Ниже приведён обновлённый компонент todo-item, который не инициирует сетевой запрос при отправке события remove-todo:
<?php
use Livewire\Component;
use App\Models\Todo;
new class extends Component {
public Todo $todo;
};
?>
<div>
<span>{{ $todo->content }}</span>
<button wire:click="$dispatch('remove-todo', { todoId: {{ $todo->id }} })">Удалить</button>
</div>
Как правило, всегда предпочитайте отправку событий на стороне клиента, когда это возможно.
Островки устраняют накладные расходы на обмен событиями
Если вы создаёте дочерние компоненты в первую очередь для того, чтобы через события обновлять родителя, рассмотрите возможность использования островков. Островки могут напрямую вызывать методы компонента без промежуточного слоя событий, поскольку они разделяют один и тот же контекст компонента.
Прямой доступ к родителю из дочернего компонента⚓︎
Обмен событиями добавляет слой косвенности. Родитель может слушать событие, которое никогда не будет отправлено из дочернего компонента, а дочерний компонент может отправить событие, которое родитель никогда не перехватит.
Такая косвенность иногда бывает желательной; однако в других случаях вам может быть удобнее напрямую обращаться к родительскому компоненту из дочернего.
Livewire позволяет это сделать, предоставляя в Blade-шаблоне дочернего компонента магическую переменную $parent, через которую вы можете напрямую обращаться к действиям и свойствам родителя. Вот как можно переписать шаблон компонента TodoItem выше, чтобы напрямую вызывать действие remove() на родителе с помощью магической переменной $parent:
<div>
<span>{{ $todo->content }}</span>
<button wire:click="$parent.remove({{ $todo->id }})">Удалить</button>
</div>
События и прямое общение с родителем — это лишь несколько способов двусторонней коммуникации между родительскими и дочерними компонентами. Понимание их компромиссов позволяет принимать более осознанные решения о том, какой паттерн использовать в конкретной ситуации.
Динамические дочерние компоненты⚓︎
Иногда вы можете не знать, какой именно дочерний компонент должен быть отрендерен на странице, до момента выполнения. Поэтому Livewire позволяет выбирать дочерний компонент во время выполнения с помощью тега <livewire:dynamic-component ...>, который принимает проп :is:
Динамические дочерние компоненты полезны в самых разных сценариях, но ниже приведён пример рендеринга разных шагов многошаговой формы с использованием динамического компонента:
<?php
use Livewire\Component;
new class extends Component {
public $current = 'step-one';
protected $steps = [
'step-one',
'step-two',
'step-three',
];
public function next()
{
$currentIndex = array_search($this->current, $this->steps);
$this->current = $this->steps[$currentIndex + 1];
}
};
?>
<div>
<livewire:dynamic-component :is="$current" :wire:key="$current" />
<button wire:click="next">Далее</button>
</div>
Теперь, если свойство $current компонента steps установлено в значение step-one, Livewire отрендерит компонент с именем «step-one» следующим образом:
<?php
use Livewire\Component;
new class extends Component {
//
};
?>
<div>
Содержимое первого шага
</div>
Если хотите, можете использовать альтернативный синтаксис:
Предупреждение
Не забудьте назначить каждому дочернему компоненту уникальный ключ. Хотя Livewire автоматически генерирует ключ для <livewire:dynamic-child /> и <livewire:is />, этот же ключ будет применён ко всем вашим дочерним компонентам, что означает, что последующие рендеры будут пропущены.
См. принудительный повторный рендер дочернего компонента, чтобы глубже понять, как ключи влияют на рендеринг компонентов.
Рекурсивные компоненты⚓︎
Хотя большинству приложений это редко требуется, компоненты Livewire можно вкладывать рекурсивно — то есть родительский компонент может рендерить сам себя в качестве дочернего.
Представьте опрос, содержащий компонент survey-question, к которому могут быть прикреплены подвопросы:
<?php
use Livewire\Attributes\Computed;
use Livewire\Component;
use App\Models\Question;
new class extends Component {
public Question $question;
#[Computed]
public function subQuestions()
{
return $this->question->subQuestions,
}
};
?>
<div>
Question: {{ $question->content }}
@foreach ($this->subQuestions as $subQuestion)
<livewire:survey-question :question="$subQuestion" :wire:key="$subQuestion->id" />
@endforeach
</div>
Предупреждение
Конечно, к рекурсивным компонентам применяются стандартные правила рекурсии. Самое важное — в вашем шаблоне должна быть логика, которая гарантирует, что шаблон не будет рекурсировать бесконечно. В примере выше, если бы $subQuestion содержал исходный вопрос в качестве своего собственного $subQuestion, произошёл бы бесконечный цикл.
Принудительный повторный рендер дочернего компонента⚓︎
За кулисами Livewire генерирует уникальный ключ для каждого вложенного Livewire-компонента в своём шаблоне.
Например, рассмотрим следующий вложенный компонент todo-count:
Livewire внутренне прикрепляет к компоненту случайную строковую ключевую строку примерно так:
Когда родительский компонент выполняет рендеринг и встречает дочерний компонент, как в примере выше, он сохраняет этот ключ в списке дочерних элементов, прикреплённых к родителю:
Livewire использует этот список как справочник при последующих рендерах, чтобы определить, был ли дочерний компонент уже отрендерен в предыдущем запросе. Если он уже был отрендерен, компонент пропускается. Помните, что вложенные компоненты независимы. Однако если ключа дочернего компонента нет в списке (то есть он ещё не был отрендерен), Livewire создаст новый экземпляр компонента и отрендерит его на месте.
Все эти нюансы — это внутреннее поведение, о котором большинству пользователей не нужно знать; однако концепция установки ключа на дочернем компоненте — это мощный инструмент для управления рендерингом дочерних элементов.
Используя эти знания, если вы хотите принудительно перерендерить компонент, достаточно просто изменить его ключ.
Ниже приведён пример, где мы можем захотеть уничтожить и заново инициализировать компонент todo-count, если переданный ему массив $todos изменился:
Как вы можете видеть выше, мы генерируем динамическую строку :key на основе содержимого $todos. Таким образом, компонент todo-count будет рендериться и существовать в обычном режиме до тех пор, пока сами $todos не изменятся. В этот момент компонент будет полностью переинициализирован с нуля, а старый экземпляр компонента будет отброшен.
Смотрите также⚓︎
- События — Обмен данными между вложенными компонентами
- Компоненты — Узнайте о рендеринге и организации компонентов
- Островки — Альтернатива вложенности для изолированных обновлений
- Понимание вложенности — Глубокое погружение в производительность и поведение вложенности
- Атрибут Reactive — Делаем пропсы реактивными во вложенных компонентах