<div x-data="{ slide: 1 }" class="border rounded"> <div> <img :src="'https://alpinejs.dev/img/morphs/morph'+slide+'.png'"> </div> <div class="flex w-full items-baseline justify-between mb-4"> <div class="w-1/2 px-4"> <button @click="slide = (slide === 1) ? 13 : slide - 1" class="w-full">Предыдущая картинка</button> </div> <div class="w-1/2 px-4"> <button @click="slide = (slide % 13) + 1" class="w-full">Следующая картинка</button> </div> </div> </div>
Morph
Плагин Morph позволяет «преобразовать» элемент на странице в предоставленный HTML-шаблон, при этом сохраняется любое состояние браузера или Alpine внутри «морфированного» элемента.
Это удобно для обновления HTML из серверного запроса без потери состояния страницы Alpine. Подобная утилита лежит в основе таких полностековых фреймворков, как Laravel Livewire и Phoenix LiveView.
Лучше всего понять его назначение можно с помощью следующей интерактивной визуализации. Попробуйте!
Установка
Вы можете использовать этот плагин, включив его из тега <script>
или установив с помощью менеджера пакетов.
Через CDN
Вы можете подключить CDN-сборку этого плагина с помощью тега <script>
, только подключать нужно ДО основного JS-файла Alpine.
<!-- Morph Plugin --><script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/morph@3/dist/cdn.min.js"></script>
<!-- Alpine Core --><script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js"></script>
<!-- Morph Plugin --><script defer src="https://unpkg.com/@alpinejs/morph@latest/dist/cdn.min.js"></script>
<!-- Alpine Core --><script defer src="https://unpkg.com/alpinejs@3/dist/cdn.min.js"></script>
Через менеджер пакетов
Вы можете установить Morph для использования внутри вашей сборки следующим образом:
npm i @alpinejs/morph
pnpm add @alpinejs/morph
yarn add @alpinejs/morph
Затем инициализируйте его в своей сборке:
import Alpine from 'alpinejs';import morph from '@alpinejs/morph';
Alpine.plugin(morph);Alpine.start();
Alpine.morph()
Функция Alpine.morph(el, newHtml)
позволяет императивно изменить узел DOM на основе переданного HTML. Она принимает следующие параметры:
Параметр | Описание |
---|---|
el | DOM-элемент на странице. |
newHtml | Строка HTML, используемая в качестве шаблона для преобразования элемента DOM. |
options (необязательный) | Объект параметров, используемый в основном для внедрения перехватчиков жизненного цикла. |
Приведём пример использования Alpine.morph()
для обновления компонента Alpine с помощью нового HTML: (В реальных приложениях этот новый HTML, скорее всего, будет поступать с сервера):
<div x-data="{ message: 'Измените меня, затем нажмите кнопку!' }" id="morph-demo-1" class="space-y-2"> <input type="text" x-model="message" /> <span x-text="message"></span></div>
<button id="morph-button-1">Преобразовать</button>
<script> document.addEventListener('alpine:init', () => { document.querySelector('#morph-button-1').addEventListener('click', () => { let el = document.querySelector('#morph-demo-1');
Alpine.morph( el, ` <div x-data="{ message: 'Измените меня, затем нажмите кнопку!' }" id="morph-demo-1" class="space-y-2"> <h4>Посмотрите, как были добавлены новые элементы</h4>
<input type="text" x-model="message"> <span x-text="message"></span>
<h4>Но состояние этого компонента не изменилось? Магия!</h4> </div> ` ); }); });</script>
Хуки жизненного цикла
Плагин «Morph» работает путём сравнения двух деревьев DOM, живого элемента и переданного в HTML.
Morph одновременно обходит оба дерева и сравнивает каждый узел и его дочерние элементы. Если он находит различия, он «исправляет» (изменяет) текущее дерево DOM, чтобы оно соответствовало переданному в дереве HTML.
Хотя алгоритм по умолчанию очень эффективен, бывают случаи, когда вам может потребоваться подключиться к его жизненному циклу и наблюдать или изменять его поведение по мере того, как оно происходит.
Прежде чем мы перейдём к самим доступным хукам жизненного цикла, давайте сначала перечислим все потенциальные параметры, которые они получают, и объясним, что представляет собой каждый из них:
Параметр или функция | Описание |
---|---|
el | Это всегда актуальный, текущий элемент DOM на странице, который будет «исправлен» (изменен Morph). |
toEl | Это «элемент шаблона». Это временный элемент, представляющий то, к чему будет привязан живой el . Он никогда не появится на странице и должен использоваться только в справочных целях. |
childrenOnly() | Это функция, которую можно вызвать внутри хука, чтобы сообщить Morph пропустить текущий элемент и «исправить» только его дочерние элементы. |
skip() | Функция, которая при вызове внутри хука «пропускает» сравнение/исправление себя и дочерних элементов текущего элемента. |
Вот доступные хуки жизненного цикла (передаются в качестве третьего параметра в Alpine.morph(..., options)
):
Наименование хука | Описание |
---|---|
updating(el, toEl, childrenOnly, skip) | Вызывается перед исправлением el с помощью сравнения toEl . |
updated(el, toEl) | Вызывается после того, как Morph исправил el . |
removing(el, skip) | Вызывается перед тем, как Morph удаляет элемент из живого DOM. |
removed(el) | Вызывается после того, как Morph удалил элемент из действующего DOM. |
adding(el, skip) | Вызывается перед добавлением нового элемента. |
added(el) | Вызывается после добавления нового элемента в действующее дерево DOM. |
key(el) | Функция многократного использования, позволяющая определить, как Morph «ключит» элементы в дереве перед сравнением/исправлением. Подробнее об этом здесь |
lookahead | Логическое значение, указывающее Morph включить дополнительную функцию в его алгоритме, которая «заглядывает вперед», чтобы гарантировать, что элемент DOM, который собирается быть удален, вместо этого должен быть просто «перемещен» в более поздний родственный элемент. |
Вот код всех этих хуков жизненного цикла для более конкретной информации:
Alpine.morph(el, newHtml, { updating(el, toEl, childrenOnly, skip) { // },
updated(el, toEl) { // },
removing(el, skip) { // },
removed(el) { // },
adding(el, skip) { // },
added(el) { // },
key(el) { // По умолчанию Alpine использует HTML-атрибут `key=""`. return el.id; },
lookahead: true, // По умолчанию: false});
Ключи
Утилиты, различающие Dom, такие как Morph, изо всех сил стараются точно «преобразовать» исходный DOM в новый HTML. Однако бывают случаи, когда невозможно определить, следует ли просто заменить элемент или заменить его полностью.
Из-за этого ограничения в Morph есть «ключевая» система, которая позволяет разработчикам «принудительно» сохранять определённые элементы, а не заменять их.
Наиболее распространённым вариантом их использования является список дочерних элементов внутри цикла. Ниже приведен пример того, почему иногда необходимы ключи:
<!-- «Живой» DOM на странице: --><ul> <li>Вася</li> <li>Петя</li> <li>Ваня</li></ul>
<!-- Новый HTML для «преобразования»: --><ul> <li>Ваня</li> <li>Вася</li> <li>Петя</li></ul>
Учитывая описанную выше ситуацию, Morph не имеет возможности узнать, что узел «Ваня» был перемещен в дереве DOM. Он просто думает, что «Вася» был изменен на «Ваня», а «Ваня» изменен на «Петя».
На самом деле это не то, чего мы хотим, мы хотим, чтобы Morph сохранял исходные элементы и вместо их изменения ПЕРЕМЕЩАЛ их внутри <ul>
.
Добавляя ключи к каждому узлу, мы можем сделать это следующим образом:
<!-- «Живой» DOM на странице: --><ul> <li key="1">Вася</li> <li key="2">Петя</li> <li key="3">Ваня</li></ul>
<!-- Новый HTML для «преобразования»: --><ul> <li key="3">Ваня</li> <li key="1">Вася</li> <li key="2">Петя</li></ul>
Теперь, когда у элементов <li>
есть «ключи», Morph сопоставит их в обоих деревьях и переместит соответствующим образом.
Вы можете настроить то, что Morph считает «ключом», с помощью опции конфигурации key:
. Подробнее об этом здесь
Проверка знаний
-
Как внедрить перехватчики жизненного цикла с помощью функции
Alpine.morph()
? -
Какой хук вызывается перед добавлением нового элемента?