JavaScript⚓︎
Использование JavaScript в компонентах Livewire⚓︎
Livewire и Alpine предоставляют множество утилит для создания динамических компонентов прямо в вашем HTML, однако бывают случаи, когда полезно выйти за пределы HTML и выполнить обычный JavaScript для вашего компонента.
Для классовых компонентов требуется директива @script
Примеры на этой странице используют обычные теги <script>, которые работают для однофайловых и многофайловых компонентов. Если вы используете классовые компоненты (где Blade-шаблон находится в отдельном файле от PHP-класса), вам необходимо обернуть свои теги script директивой @script:
Это указывает Livewire правильно обрабатывать время выполнения для классовых компонентов.
Выполнение скриптов⚓︎
Вы можете добавлять теги <script> непосредственно внутри шаблона вашего компонента, чтобы выполнять JavaScript при загрузке компонента.
Поскольку эти скрипты обрабатываются Livewire, они выполняются в идеальный момент — после загрузки страницы, но до того, как компонент Livewire отрендерится. Это означает, что вам больше не нужно оборачивать свои скрипты в document.addEventListener('...'), чтобы они корректно загружались.
Благодаря этому даже лениво загружаемые (lazy) или условно отображаемые компоненты Livewire всё равно могут выполнять JavaScript после инициализации страницы.
<div>
...
</div>
<script>
// Этот JavaScript будет выполняться каждый раз, когда данный компонент загружается на страницу...
</script>
Вот более полный пример, где вы можете, например, зарегистрировать JavaScript-действие, которое затем используется в вашем компоненте Livewire.
<div>
<button wire:click="$js.increment">+</button>
</div>
<script>
this.$js.increment = () => {
console.log('increment')
}
</script>
Чтобы узнать больше о JavaScript-действиях, посетите документацию по действиям.
Использование $wire из скриптов⚓︎
Когда вы добавляете теги <script> внутрь своего компонента, у вас автоматически появляется доступ к объекту $wire вашего компонента Livewire.
Вот пример использования простого setInterval для обновления компонента каждые 2 секунды (это можно легко сделать с помощью wire:poll, но это простой способ продемонстрировать идею):
Объект $wire⚓︎
Объект $wire — это ваш JavaScript-интерфейс к компоненту Livewire. Он предоставляет доступ к свойствам компонента, методам и утилитам для взаимодействия с сервером.
Внутри скриптов компонента вы можете использовать $wire напрямую. Вот наиболее важные методы и возможности, которые вы будете использовать:
// Доступ и изменение свойств
$wire.count
$wire.count = 5
$wire.$set('count', 5)
// Вызов методов компонента
$wire.save()
$wire.delete(postId)
// Обновление компонента
$wire.$refresh()
// Отправка событий
$wire.$dispatch('post-created', { postId: 2 })
// Прослушивание событий
$wire.$on('post-created', (event) => {
console.log(event.postId)
})
// Доступ к корневому элементу
$wire.$el.querySelector('.modal')
Полный справочник по $wire
Для полного списка всех методов и свойств объекта $wire смотрите справочник $wire внизу этой страницы.
Загрузка ресурсов⚓︎
Теги <script> внутри компонента удобны для выполнения небольшого количества JavaScript каждый раз, когда компонент Livewire загружается, однако бывают случаи, когда вам нужно загрузить на страницу целые библиотеки скриптов и стилей вместе с компонентом.
Вот пример использования директивы @assets для загрузки библиотеки выбора дат Pikaday и её инициализации внутри вашего компонента:
<div>
<input type="text" data-picker>
</div>
@assets
<script src="https://cdn.jsdelivr.net/npm/pikaday/pikaday.js" defer></script>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/pikaday/css/pikaday.css">
@endassets
<script>
new Pikaday({ field: $wire.$el.querySelector('[data-picker]') });
</script>
Когда этот компонент загружается, Livewire позаботится о том, чтобы все @assets были загружены на страницу до выполнения скриптов. Кроме того, указанные @assets будут загружены только один раз на страницу, независимо от того, сколько экземпляров этого компонента присутствует на странице — в отличие от скриптов компонента, которые будут выполняться для каждого экземпляра компонента на странице.
Перехватчики⚓︎
Перехватывайте запросы Livewire на трёх уровнях: action (самый детальный), message (по компоненту) и request (уровень HTTP).
// Перехватчики действий — срабатывают для каждого вызова действия
$wire.intercept(callback) // Все действия этого компонента
$wire.intercept('save', callback) // Только действие 'save'
Livewire.interceptAction(callback) // Глобально (для всех компонентов)
// Перехватчики сообщений — срабатывают для каждого сообщения компонента
$wire.interceptMessage(callback) // Сообщения от этого компонента
$wire.interceptMessage('save', callback) // Только когда сообщение содержит 'save'
Livewire.interceptMessage(callback) // Глобально (для всех компонентов)
// Перехватчики запросов — срабатывают для каждого HTTP-запроса
$wire.interceptRequest(callback) // Запросы, в которых участвует этот компонент
$wire.interceptRequest('save', callback) // Только когда запрос содержит 'save'
Livewire.interceptRequest(callback) // Глобально (для всех запросов)
Все перехватчики возвращают функцию отписки (unsubscribe):
Перехватчики действий⚓︎
Перехватчики действий — самые детализированные. Они срабатывают для каждого вызова метода компонента.
$wire.intercept(({ action, onSend, onCancel, onSuccess, onError, onFailure, onFinish }) => {
// action.name - Имя метода ('save', '$refresh' и т.д.)
// action.params - Параметры метода
// action.component - Экземпляр компонента
// action.cancel() - Отменить это действие
onSend(({ call }) => {
// call: { method, params, metadata }
})
onCancel(() => {})
onSuccess((result) => {
// result: Возвращаемое значение из PHP-метода
})
onError(({ response, body, preventDefault }) => {
preventDefault() // Предотвратить показ модального окна с ошибкой
})
onFailure(({ error }) => {
// error: Сетевая ошибка
})
onFinish(() => {
// Выполняется после завершения морфинга DOM (или при ошибке/отмене)
})
})
Перехватчики сообщений⚓︎
Перехватчики сообщений срабатывают при каждом обновлении компонента. Сообщение может содержать одно или несколько действий.
$wire.interceptMessage(({ message, cancel, onSend, onCancel, onSuccess, onSkipped, onError, onFailure, onStream, onFinish }) => {
// message.component - Экземпляр компонента
// message.actions - Набор действий в этом сообщении
// message.isSkipped() - True, если сервер пропустил это сообщение
// cancel() - Отменить это сообщение
onSend(({ payload }) => {
// payload: { snapshot, updates, calls }
})
onCancel(() => {})
onSuccess(({ payload, onSync, onEffect, onMorph, onRender }) => {
// payload: { snapshot, effects }
onSync(() => {}) // После синхронизации состояния
onEffect(() => {}) // После обработки эффектов
onMorph(async () => {}) // После морфинга DOM (должен быть асинхронным)
onRender(() => {}) // После завершения рендеринга
})
onSkipped(() => {
// Сервер намеренно пропустил это сообщение (например, неизменённый
// реактивный дочерний компонент). Нет payload, морфинга, рендера —
// но промисы действий всё равно разрешаются. Используйте для телеметрии или dev-инструментов.
})
onError(({ response, body, preventDefault }) => {
preventDefault() // Предотвратить показ модального окна с ошибкой
})
onFailure(({ error }) => {})
onStream(({ json }) => {
// json: Распарсенный фрагмент потока
})
onFinish(() => {
// Выполняется после завершения морфинга DOM (или при ошибке/отмене/пропуске)
})
})
Тайминг⚓︎
Порядок выполнения хуков для успешных запросов:
onSuccess— Сразу после получения ответа от сервераonSync— После слияния состоянияonEffect— После обработки эффектовonMorph— После морфинга DOMonFinish— После завершения морфингаonRender— ВнутриrequestAnimationFrame(после отрисовки)
Для пропущенных сообщений (например, неизменённый реактивный дочерний компонент) вместо onSuccess срабатывает onSkipped, затем onFinish. Хуки морфинга/рендеринга не вызываются, так как применять нечего.
Промисы действий (.then()) разрешаются одновременно с onFinish (после морфинга или мгновенно при пропуске).
Перехватчики запросов⚓︎
Перехватчики запросов срабатывают для каждого HTTP-запроса. Один запрос может содержать сообщения от нескольких компонентов.
$wire.interceptRequest(({ request, onSend, onCancel, onSuccess, onError, onFailure, onResponse, onParsed, onStream, onRedirect, onDump, onFinish }) => {
// request.messages — Набор сообщений в этом запросе
// request.cancel() — Отменить этот запрос
onSend(({ responsePromise }) => {})
onCancel(() => {})
onResponse(({ response }) => {
// response: Объект Fetch Response (до чтения тела ответа)
})
onParsed(({ response, body }) => {
// body: Тело ответа в виде строки
})
onSuccess(({ response, body, json }) => {})
onError(({ response, body, preventDefault }) => {
preventDefault() // Предотвратить показ модального окна с ошибкой
})
onFailure(({ error }) => {})
onStream(({ response }) => {})
onRedirect(({ url, preventDefault }) => {
preventDefault() // Предотвратить перенаправление
})
onDump(({ html, preventDefault }) => {
preventDefault() // Предотвратить показ модального окна с дампом
})
onFinish(() => {})
})
Примеры⚓︎
Состояние загрузки для компонента:
<script>
$wire.intercept(({ onSend, onFinish }) => {
onSend(() => $wire.$el.classList.add('opacity-50'))
onFinish(() => $wire.$el.classList.remove('opacity-50'))
})
</script>
Подтверждение перед удалением:
<script>
$wire.intercept('delete', ({ action }) => {
if (!confirm('Are you sure?')) {
action.cancel()
}
})
</script>
Обработка истечения глобальной сессии:
Livewire.interceptRequest(({ onError }) => {
onError(({ response, preventDefault }) => {
if (response.status === 419) {
preventDefault()
if (confirm('Session expired. Refresh?')) {
window.location.reload()
}
}
})
})
Уведомление об успешном выполнении конкретного действия:
<script>
$wire.intercept('save', ({ onSuccess, onError }) => {
onSuccess(() => showToast('Saved!'))
onError(() => showToast('Failed to save', 'error'))
})
</script>
Глобальные события Livewire⚓︎
Livewire отправляет два полезных браузерных события, которые вы можете использовать для регистрации любых пользовательских точек расширения из внешних скриптов:
<script>
document.addEventListener('livewire:init', () => {
// Выполняется после загрузки Livewire, но до его инициализации
// на странице...
})
document.addEventListener('livewire:initialized', () => {
// Выполняется сразу после того, как Livewire полностью завершил
// инициализацию на странице...
})
</script>
Информация
Часто полезно регистрировать любые пользовательские директивы или хуки жизненного цикла внутри события livewire:init, чтобы они были доступны до того, как Livewire начнёт инициализацию на странице.
Глобальный объект Livewire⚓︎
Глобальный объект Livewire — это наилучшая отправная точка для взаимодействия с Livewire из внешних скриптов.
Вы можете получить доступ к глобальному объекту JavaScript Livewire через window из любого места в вашем клиентском коде.
Часто удобно использовать window.Livewire внутри слушателя события livewire:init.
Доступ к компонентам⚓︎
Вы можете использовать следующие методы, чтобы получить доступ к конкретным компонентам Livewire, загруженным на текущей странице:
// Получаем объект $wire для первого компонента на странице...
let component = Livewire.first()
// Получаем объект $wire конкретного компонента по его ID...
let component = Livewire.find(id)
// Получаем массив объектов $wire компонентов по имени...
let components = Livewire.getByName(name)
// Получаем объекты $wire для всех компонентов на странице...
let components = Livewire.all()
Информация
Каждый из этих методов возвращает объект $wire, представляющий состояние компонента в Livewire.
Подробнее об этих объектах можно узнать в документации по $wire.
Взаимодействие с событиями⚓︎
Помимо отправки и прослушивания событий из отдельных компонентов в PHP, глобальный объект Livewire позволяет взаимодействовать с системой событий Livewire из любого места вашего приложения:
// Отправляем событие всем компонентам Livewire, которые его прослушивают...
Livewire.dispatch('post-created', { postId: 2 })
// Отправляем событие конкретному компоненту Livewire по его имени...
Livewire.dispatchTo('dashboard', 'post-created', { postId: 2 })
// Прослушиваем события, отправленные из компонентов Livewire...
Livewire.on('post-created', ({ postId }) => {
// ...
})
В определённых сценариях может потребоваться отменить регистрацию глобальных событий Livewire. Например, при работе с компонентами Alpine и директивой wire:navigate может происходить многократная регистрация слушателей, поскольку метод init вызывается при каждой навигации между страницами. Чтобы решить эту проблему, используйте функцию destroy, которую Alpine автоматически вызывает при уничтожении компонента. Внутри этой функции пройдитесь по всем вашим слушателям и отмените их регистрацию, чтобы предотвратить нежелательное накопление.
Alpine.data('MyComponent', () => ({
listeners: [],
init() {
this.listeners.push(
Livewire.on('post-created', (options) => {
// Делаем что-нибудь...
})
);
},
destroy() {
this.listeners.forEach((listener) => {
listener();
});
}
}));
Использование хуков жизненного цикла⚓︎
Livewire позволяет вам подключаться к различным частям его глобального жизненного цикла с помощью метода Livewire.hook():
// Регистрируем колбэк, который будет выполнен при срабатывании определённого внутреннего хука Livewire...
Livewire.hook('component.init', ({ component, cleanup }) => {
// ...
})
Более подробную информацию о JavaScript-хуках Livewire можно найти ниже.
Регистрация пользовательских директив⚓︎
Livewire позволяет регистрировать пользовательские директивы с помощью метода Livewire.directive().
Ниже приведён пример пользовательской директивы wire:confirm, которая использует диалоговое окно JavaScript confirm() для подтверждения или отмены действия перед отправкой на сервер:
Вот реализация директивы wire:confirm с использованием Livewire.directive():
Livewire.directive('confirm', ({ el, directive, component, cleanup }) => {
let content = directive.expression
// Объект "directive" предоставляет доступ к разобранной директиве.
// Например, вот его значения для: wire:click.prevent="deletePost(1)"
//
// directive.raw = wire:click.prevent
// directive.value = "click"
// directive.modifiers = ['prevent']
// directive.expression = "deletePost(1)"
let onClick = e => {
if (! confirm(content)) {
e.preventDefault()
e.stopImmediatePropagation()
}
}
el.addEventListener('click', onClick, { capture: true })
// Зарегистрируйте любой код очистки внутри `cleanup()` на случай,
// если компонент Livewire будет удалён из DOM,
// пока страница всё ещё активна.
cleanup(() => {
el.removeEventListener('click', onClick)
})
})
JavaScript-хуки⚓︎
Для продвинутых пользователей Livewire предоставляет доступ к своей внутренней системе клиентских «хуков». Вы можете использовать следующие хуки, чтобы расширить функциональность Livewire или получить больше информации о вашем приложении Livewire.
Инициализация компонента⚓︎
Каждый раз, когда Livewire обнаруживает новый компонент — будь то при начальной загрузке страницы или позже — срабатывает событие component.init. Вы можете подключиться к хуку component.init, чтобы перехватить или инициализировать что-либо, связанное с новым компонентом:
Для получения дополнительной информации, пожалуйста, обратитесь к документации по объекту компонента.
Инициализация DOM-элементов⚓︎
Помимо отправки события при инициализации новых компонентов, Livewire также отправляет событие для каждого DOM-элемента внутри конкретного компонента Livewire.
Это можно использовать для предоставления пользовательских HTML-атрибутов Livewire в вашем приложении:
Хуки морфинга DOM⚓︎
Во время фазы морфинга DOM — которая происходит после того, как Livewire завершает сетевой цикл (запрос-ответ) — Livewire инициирует серию событий для каждого элемента, который подвергается изменениям.
Livewire.hook('morph.updating', ({ el, component, toEl, skip, childrenOnly }) => {
//
})
Livewire.hook('morph.updated', ({ el, component }) => {
//
})
Livewire.hook('morph.removing', ({ el, component, skip }) => {
//
})
Livewire.hook('morph.removed', ({ el, component }) => {
//
})
Livewire.hook('morph.adding', ({ el, component }) => {
//
})
Livewire.hook('morph.added', ({ el }) => {
//
})
В дополнение к событиям, инициируемым для каждого элемента, события morph и morphed вызываются для каждого компонента Livewire:
Livewire.hook('morph', ({ el, component }) => {
// Выполняется непосредственно перед морфингом дочерних элементов в `component` (исключая частичный морфинг)
})
Livewire.hook('morphed', ({ el, component }) => {
// Выполняется после того, как все дочерние элементы в `component` прошли морфинг (исключая частичный морфинг)
})
Выполнение JavaScript на стороне сервера⚓︎
В дополнение к выполнению JavaScript непосредственно в ваших компонентах, вы можете использовать метод js() для вычисления JavaScript-выражений из вашего PHP-кода на стороне сервера.
Это обычно полезно для выполнения каких-либо ответных действий на стороне клиента после завершения действия на стороне сервера.
Например, вот компонент post.create, который вызывает диалоговое окно оповещения на стороне клиента после того, как пост будет сохранен в базе данных:
<?php
use Livewire\Component;
new class extends Component {
public $title = '';
public function save()
{
// Сохраняем пост в базу данных...
$this->js("alert('Пост сохранён!')");
}
};
JavaScript-выражение alert('Пост сохранён!') будет выполнено на клиенте после того, как пост будет сохранен в базе данных на сервере.
Вы можете получить доступ к объекту $wire текущего компонента внутри выражения:
<?php
$this->js('$wire.$refresh()');
$this->js('$wire.$dispatch("post-created", { id: ' . $post->id . ' })');
Распространённые паттерны⚓︎
Ниже приведены некоторые распространённые паттерны использования JavaScript с Livewire в реальных приложениях.
Интеграция сторонних библиотек⚓︎
Многие JavaScript-библиотеки требуют инициализации при добавлении элементов на страницу. Используйте скрипты компонентов для инициализации библиотек при загрузке вашего компонента:
<div>
<div id="map" style="height: 400px;"></div>
</div>
@assets
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY"></script>
@endassets
<script>
new google.maps.Map($wire.$el.querySelector('#map'), {
center: { lat: {{ $latitude }}, lng: {{ $longitude }} },
zoom: 12
});
</script>
Синхронизация с localStorage⚓︎
Вы можете синхронизировать состояние компонента с localStorage, используя $watch:
<script>
// Загрузка из localStorage при инициализации
if (localStorage.getItem('draft')) {
$wire.content = localStorage.getItem('draft');
}
// Сохранение в localStorage при изменении
$wire.$watch('content', (value) => {
localStorage.setItem('draft', value);
});
</script>
Использование директивы @js⚓︎
Если вам нужно вывести данные PHP для непосредственного использования в JavaScript, вы можете использовать директиву @js.
<script>
let posts = @js($posts)
// "posts" теперь будет JavaScript-массивом данных постов из PHP.
</script>
Лучшие практики⚓︎
Скрипты компонентов против глобальных скриптов⚓︎
Используйте скрипты компонентов, когда:
- JavaScript специфичен для функциональности конкретного компонента
- Вам нужен доступ к
$wireили данным, специфичным для компонента - Код должен запускаться каждый раз при загрузке компонента
Используйте глобальные скрипты, когда:
- Регистрируете пользовательские директивы или хуки
- Настраиваете глобальные слушатели событий
- Инициализируете JavaScript уровня всего приложения
Предотвращение утечек памяти⚓︎
При добавлении слушателей событий в скрипты компонентов Livewire автоматически очищает их при удалении компонента. Однако, если вы используете глобальные перехватчики (interceptors) или хуки, не забудьте обеспечить очистку там, где это уместно:
// На уровне компонента — автоматически очищается ✓
$wire.intercept(({ onSend }) => {
onSend(() => console.log('Sending...'));
});
// На глобальном уровне — живет в течение всего жизненного цикла страницы
Livewire.interceptMessage(({ onSend }) => {
onSend(() => console.log('Sending...'));
});
Советы по отладке⚓︎
Доступ к компоненту из консоли браузера:
// Получить первый компонент на странице
let $wire = Livewire.first()
// Проверить состояние компонента
console.log($wire.count)
// Вызвать методы
$wire.increment()
Мониторинг всех запросов:
Livewire.interceptRequest(({ onSend }) => {
onSend(() => {
console.log('Запрос отправлен:', Date.now());
});
});
Просмотр снимков компонентов:
Соображения по производительности⚓︎
- Используйте
wire:ignoreдля элементов, которые не должны изменяться при морфинге DOM в Livewire - Дебансируйте дорогие операции с помощью
wire:model.debounceили debounce в JavaScript - Используйте ленивую загрузку (параметр
lazy) для компонентов, которые не видны сразу - Рассмотрите использование островов (islands) для изолированных областей, которые обновляются независимо
Смотрите также⚓︎
- Стили — Добавление локальных (scoped) CSS к вашим компонентам
- Alpine — Использование Alpine для интерактивности на стороне клиента
- Действия — Создание JavaScript-действий в компонентах
- Свойства — Доступ к свойствам из JavaScript через $wire
- События — Отправка и прослушивание событий в JavaScript
Справочник⚓︎
При расширении JavaScript-системы Livewire важно понимать различные объекты, с которыми вы можете столкнуться.
Ниже приведён исчерпывающий справочник всех релевантных внутренних свойств Livewire.
Напоминаем: среднестатистический пользователь Livewire может никогда не взаимодействовать с этими объектами. Большинство из них предназначены для внутренней системы Livewire или для продвинутых пользователей.
Объект $wire⚓︎
Рассмотрим следующий обобщённый компонент Counter:
<?php
namespace App\Livewire;
use Livewire\Component;
class Counter extends Component
{
public $count = 1;
public function increment()
{
$this->count++;
}
public function render()
{
return view('livewire.counter');
}
}
Livewire предоставляет JavaScript-представление серверного компонента в виде объекта, который обычно называют $wire:
let $wire = {
// Все публичные свойства компонента доступны напрямую через $wire...
count: 0,
// Все публичные методы открыты и могут быть вызваны через $wire...
increment() { ... },
// Доступ к объекту `$wire` родительского компонента, если он существует...
$parent,
// Доступ к корневому DOM-элементу компонента Livewire...
$el,
// Доступ к ID текущего компонента Livewire...
$id,
// Получить значение свойства по имени...
// Использование: $wire.$get('count')
$get(name) { ... },
// Установить свойство компонента по имени...
// Использование: $wire.$set('count', 5)
$set(name, value, live = true) { ... },
// Переключить значение логического свойства...
$toggle(name, live = true) { ... },
// Вызвать метод...
// Использование: $wire.$call('increment')
$call(method, ...params) { ... },
// Определить действие JavaScript...
// Использование: $wire.$js('increment', () => { ... })
// Использование: $wire.$js.increment = () => { ... }
$js(name, callback) { ... },
// [УСТАРЕЛО] Entangle — скорее всего, вам это не понадобится.
// Вместо этого используйте $wire напрямую для доступа к свойствам.
// Использование: <div x-data="{ count: $wire.$entangle('count') }">
$entangle(name, live = false) { ... },
// Отслеживать изменения значения свойства...
// Использование: Alpine.$watch('count', (value, old) => { ... })
$watch(name, callback) { ... },
// Ограничить следующее действие именованным «островом» (island)...
// Возвращает $wire, чтобы вы могли строить цепочки вызовов методов.
// Действие в цепочке приведет к повторному рендерингу только указанного «острова».
// Использование: $wire.$island('revenue').$refresh()
// Использование: $wire.$island('feed', { mode: 'append' }).loadMore()
$island(name, options = {}) { ... },
// Обновить компонент, отправив сообщение на сервер
// для повторного рендерингу HTML и его замены на странице...
$refresh() { ... },
// Идентично $refresh выше. Просто более техническое название...
$commit() { ... }, // Псевдоним для $refresh()
// Слушать событие, отправленное из этого компонента или его дочерних элементов...
// Использование: $wire.$on('post-created', () => { ... })
$on(event, callback) { ... },
// Слушать хук жизненного цикла, вызванный этим компонентом или запросом...
// Использование: $wire.$hook('message.sent', () => { ... })
$hook(name, callback) { ... },
// Отправить событие из этого компонента...
// Использование: $wire.$dispatch('post-created', { postId: 2 })
$dispatch(event, params = {}) { ... },
// Отправить событие другому компоненту...
// Использование: $wire.$dispatchTo('dashboard', 'post-created', { postId: 2 })
$dispatchTo(otherComponentName, event, params = {}) { ... },
// Отправить событие только этому компоненту и никаким другим...
$dispatchSelf(event, params = {}) { ... },
// JavaScript API для загрузки файла напрямую в компонент,
// а не через `wire:model`...
$upload(
name, // Имя свойства
file, // JavaScript-объект File
finish = () => { ... }, // Выполняется, когда загрузка завершена...
error = () => { ... }, // Выполняется, если во время загрузки произошла ошибка...
progress = (event) => { // Выполняется по мере процесса загрузки...
event.detail.progress // Целое число от 1 до 100...
},
) { ... },
// API для одновременной загрузки нескольких файлов...
$uploadMultiple(name, files, finish, error, progress) { },
// Удалить загрузку после того, как она была временно загружена, но не сохранена...
$removeUpload(name, tmpFilename, finish, error) { ... },
// Зарегистрировать перехватчик действия для этого экземпляра компонента
// Использование: $wire.intercept(({ action, onSend, onCancel, onSuccess, onError, onFailure, onFinish }) => { ... })
// Или ограничить конкретным действием: $wire.intercept('save', ({ action, onSuccess }) => { ... })
intercept(actionOrCallback, callback) { ... },
// Псевдоним для intercept
interceptAction(actionOrCallback, callback) { ... },
// Зарегистрировать перехватчик сообщения для этого экземпляра компонента
// Использование: $wire.interceptMessage(({ message, cancel, onSend, onCancel, onSuccess, onSkipped, onError, onFailure, onFinish }) => { ... })
// Или ограничить конкретным действием: $wire.interceptMessage('save', callback)
interceptMessage(actionOrCallback, callback) { ... },
// Зарегистрировать перехватчик запроса для этого экземпляра компонента
// Использование: $wire.interceptRequest(({ request, onSend, onCancel, onSuccess, onError, onFailure, onFinish }) => { ... })
// Или ограничить конкретным действием: $wire.interceptRequest('save', callback)
interceptRequest(actionOrCallback, callback) { ... },
// Получить базовый объект «component»...
__instance() { ... },
}
Вы можете узнать больше о $wire в документации Livewire по доступу к свойствам в JavaScript.
Объект snapshot⚓︎
Между сетевыми запросами Livewire сериализует PHP-компонент в объект, который может быть использован в JavaScript. Этот снимок (snapshot) используется для десериализации компонента обратно в PHP-объект и, следовательно, имеет встроенные механизмы для предотвращения несанкционированного вмешательства:
let snapshot = {
// Сериализованное состояние компонента (публичные свойства)...
data: { count: 0 },
// Постоянная информация о компоненте...
memo: {
// Уникальный ID компонента...
id: '0qCY3ri9pzSSMIXPGg8F',
// Имя компонента. Например, <livewire:[name] />
name: 'counter',
// URI, метод и локаль веб-страницы, на которой изначально
// был загружен компонент. Это используется для повторного
// применения любых посредников (middleware) из исходного запроса
// к последующим запросам на обновление компонента (сообщениям)...
path: '/',
method: 'GET',
locale: 'en',
// Список любых вложенных «дочерних» компонентов. Ключами является
// внутренний ID шаблона, а значениями — ID компонентов...
children: [],
// Был ли этот компонент загружен «лениво» (lazy loaded)...
lazyLoaded: false,
// Список любых ошибок валидации, возникших во время
// последнего запроса...
errors: [],
},
// Надежно зашифрованный хэш этого снимка. Таким образом,
// если злоумышленник попытается подделать снимок с целью
// получения доступа к не принадлежащим ему ресурсам на сервере,
// проверка контрольной суммы не удастся и будет выдана ошибка...
checksum: '1bc274eea17a434e33d26bcaba4a247a4a7768bd286456a83ea6e9be2d18c1e7',
}
Объект component⚓︎
У каждого компонента на странице есть соответствующий объект компонента, который за кулисами отслеживает его состояние и предоставляет доступ к его базовой функциональности. Это на один уровень глубже, чем $wire. Это предназначено только для продвинутого использования.
Ниже представлен реальный объект компонента для вышеупомянутого компонента Counter с описанием соответствующих свойств в комментариях JS:
let component = {
// Корневой HTML-элемент компонента...
el: HTMLElement,
// Уникальный ID компонента...
id: '0qCY3ri9pzSSMIXPGg8F',
// «Имя» компонента (<livewire:[name] />)...
name: 'counter',
// Последний объект «эффектов». Эффекты — это «побочные эффекты» от
// сетевых циклов (запрос-ответ) сервера. К ним относятся редиректы,
// загрузки файлов и т.д...
effects: {},
// Последнее известное состояние компонента на стороне сервера...
canonical: { count: 0 },
// Изменяемый объект данных компонента, представляющий его
// текущее состояние на стороне клиента...
ephemeral: { count: 0 },
// Реактивная версия `this.ephemeral`. Изменения в этом объекте
// будут подхватываться выражениями AlpineJS...
reactive: Proxy,
// Прокси-объект, который обычно используется внутри выражений
// Alpine как `$wire`. Предназначен для предоставления удобного
// интерфейса JS-объекта для компонентов Livewire...
$wire: Proxy,
// Список любых вложенных «дочерних» компонентов. Ключами является
// внутренний ID шаблона, а значениями — ID компонентов...
children: [],
// Последнее известное представление этого компонента в виде «снимка» (snapshot).
// Снимки создаются из серверного компонента и используются для
// воссоздания PHP-объекта на бэкенде...
snapshot: {...},
// Неразобранная версия вышеуказанного снимка. Она используется для
// отправки обратно на сервер при следующем сетевом цикле, так как
// парсинг в JS нарушает кодировку PHP, что часто приводит к
// несовпадению контрольных сумм.
snapshotEncoded: '{"data":{"count":0},"memo":{"id":"0qCY3ri9pzSSMIXPGg8F","name":"counter","path":"\/","method":"GET","children":[],"lazyLoaded":true,"errors":[],"locale":"en"},"checksum":"1bc274eea17a434e33d26bcaba4a247a4a7768bd286456a83ea6e9be2d18c1e7"}',
}
Полезная нагрузка message⚓︎
Когда в браузере выполняется действие над компонентом Livewire, инициируется сетевой запрос. Этот сетевой запрос содержит один или несколько компонентов и различные инструкции для сервера. Внутренне эти сетевые полезные нагрузки компонентов называются «сообщениями».
«Сообщение» представляет собой данные, отправляемые с фронтенда на бэкенд, когда компоненту требуется обновление. Компонент отрисовывается и управляется на фронтенде до тех пор, пока не будет выполнено действие, требующее отправки сообщения с его состоянием и обновлениями на бэкенд.
Вы узнаете эту схему по полезной нагрузке во вкладке Network (Сеть) в инструментах разработчика вашего браузера или по JavaScript-хукам Livewire: