Обновление с версии v2
Перед вами исчерпывающее руководство по основным изменениям в Alpine v3, но если вы предпочитаете что-то более интересное, то можете просмотреть все изменения и новые функции в v3 в докладе Alpine Day 2021 «Будущее Alpine»:
Обновление с Alpine v2 до v3 — это плавный переход, который не потребует от вас больших усилий. В большинстве случаев вам даже не придется вносить изменения в свой код, чтобы начать использовать v3. Тем не менее, ознакомьтесь со списком наиболее значимых изменений, которые с большей вероятностью могут повлиять на ваше приложение:
$el всегда ссылается на элемент, над которым выполнено выражение, а не на корневой элемент компонента. Это уменьшает необходимость использования x-ref. Если же вам всё-таки нужно получить доступ к корневому элементу компонента, используйте $root. Например:
<!-- 🚫 До --><div x-data> <button @click="console.log($el)"></button> <!-- В v2 $el был <div>, теперь это <button>. --></div>
<!-- ✅ После --><div x-data> <button @click="console.log($root)"></button></div>Для более плавного обновления можно заменить все экземпляры $el на пользовательскую магию $root.
Автоматическое выполнение функций init(), определённых в объекте data
Заголовок раздела «Автоматическое выполнение функций init(), определённых в объекте data»Распространённой схемой в v2 был ручной вызов метода init() (или аналогичного по названию метода) для объекта x-data.
В v3 Alpine будет автоматически вызывать методы init() у объектов данных.
<!-- 🚫 До --><div x-data="foo()" x-init="init()"></div>
<!-- ✅ После --><div x-data="foo()"></div>
<script> function foo() { return { init() { // }, }; }</script>Если вы импортировали Alpine v2 через NPM, теперь для работы с v3 вам нужно будет вручную вызывать Alpine.start(). Это не затронет пользователей, которые используют файл сборки Alpine или CDN через тег <template>.
// 🚫 Доimport 'alpinejs';
// ✅ Послеimport Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();Вместо x-show.transition теперь используется x-transition
Заголовок раздела «Вместо x-show.transition теперь используется x-transition»Все возможности, которые предоставляли хелперы x-show.transition..., остаются доступными, но теперь через более унифицированный API — x-transition.
<!-- 🚫 До --><div x-show.transition="open"></div><!-- ✅ После --><div x-show="open" x-transition></div>
<!-- 🚫 До --><div x-show.transition.duration.500ms="open"></div><!-- ✅ После --><div x-show="open" x-transition.duration.500ms></div>
<!-- 🚫 До --><div x-show.transition.in.duration.500ms.out.duration.750ms="open"></div><!-- ✅ После --><div x-show="open" x-transition:enter.duration.500ms x-transition:leave.duration.750ms></div>x-if больше не поддерживает x-transition
Заголовок раздела «x-if больше не поддерживает x-transition»Возможность перемещать элементы и добавлять их до/после удаления из DOM больше не доступна в Alpine.
Это была функция, о существовании которой даже мало кто знал, не говоря уже об использовании.
Поскольку система переходов сложна, с точки зрения обслуживания имеет смысл поддерживать переходные элементы только с помощью x-show.
<!-- 🚫 До --><template x-if.transition="open"> <div>...</div></template>
<!-- ✅ После --><div x-show="open" x-transition>...</div>x-data имеет каскадную область видимости
Заголовок раздела «x-data имеет каскадную область видимости»Область, определённая в x-data, теперь доступна всем дочерним элементам, если она не перезаписана вложенным выражением x-data.
<!-- 🚫 До --><div x-data="{ foo: 'bar' }"> <div x-data="{}"> <!-- foo не определено --> </div></div>
<!-- ✅ После --><div x-data="{ foo: 'bar' }"> <div x-data="{}"> <!-- foo равно 'bar' --> </div></div>x-init больше не принимает функцию обратного вызова
Заголовок раздела «x-init больше не принимает функцию обратного вызова»До v3, если x-init получал возвращаемое значение, являющееся «функцией» typeof, он выполнял обратный вызов после того, как Alpine завершал инициализацию всех остальных директив в дереве. Теперь вам нужно вручную вызывать $nextTick(), чтобы добиться такого поведения. x-init больше не «распознает возвращаемое значение».
<!-- 🚫 До --><div x-data x-init="() => { ... }">...</div>
<!-- ✅ После --><div x-data x-init="$nextTick(() => { ... })">...</div>Возврат false из обработчиков событий больше не приводит к неявному preventDefault
Заголовок раздела «Возврат false из обработчиков событий больше не приводит к неявному preventDefault»Alpine v2 воспринимает возвращаемое значение false как желание запустить preventDefault для события. Это соответствует стандартному поведению встроенных слушателей: <... oninput="someFunctionThatReturnsFalse()">. Alpine v3 больше не поддерживает этот API. Большинство людей не знают о его существовании, и поэтому поведение является удивительным.
<!-- 🚫 До --><div x-data="{ blockInput() { return false } }"> <input type="text" @input="blockInput()" /></div>
<!-- ✅ После --><div x-data="{ blockInput(e) { e.preventDefault() } }"> <input type="text" @input="blockInput($event)" /></div>Вместо x-spread теперь используется x-bind
Заголовок раздела «Вместо x-spread теперь используется x-bind»Одна из историй повторного использования функциональности в Alpine — абстрагирование директив Alpine в объекты и применение их к элементам с помощью x-spread. Это поведение осталось прежним, за исключением того, что теперь вместо x-spread нужно использовать x-bind.
<!-- 🚫 До --><div x-data="dropdown()"> <button x-spread="trigger">Переключить</button>
<div x-spread="dialogue">...</div></div>
<!-- ✅ После --><div x-data="dropdown()"> <button x-bind="trigger">Переключить</button>
<div x-bind="dialogue">...</div></div>
<script> function dropdown() { return { open: false,
trigger: { 'x-on:click'() { this.open = !this.open; }, },
dialogue: { 'x-show'() { return this.open; }, 'x-bind:class'() { return 'foo bar'; }, }, }; }</script>Использование глобальных событий жизненного цикла вместо Alpine.deferLoadingAlpine()
Заголовок раздела «Использование глобальных событий жизненного цикла вместо Alpine.deferLoadingAlpine()»<!-- 🚫 До --><script> window.deferLoadingAlpine = (startAlpine) => { // Будет выполнено перед инициализацией Alpine.
startAlpine();
// Будет выполнено после инициализации Alpine. };</script>
<!-- ✅ После --><script> document.addEventListener('alpine:init', () => { // Будет выполнено перед инициализацией Alpine. });
document.addEventListener('alpine:initialized', () => { // Будет выполнено после инициализации Alpine. });</script>В Alpine v2 для кода ниже
<div x-data="{options: [{value: 1}, {value: 2}, {value: 3}] }"> <div x-ref="0">0</div> <template x-for="option in options"> <div :x-ref="option.value" x-text="option.value"></div> </template>
<button @click="console.log($refs[0], $refs[1], $refs[2], $refs[3]);">Отобразить $refs</button></div>после нажатия кнопки отображались все $refs. Однако в Alpine v3 возможен доступ только к $refs для элементов, созданных статически, поэтому, как и ожидалось, будет возвращена только первая ссылка.
Alpine больше не поддерживает Internet Explorer 11. Если вам нужна поддержка IE11, продолжайте использовать Alpine v2.
Следующие два API по-прежнему будут работать в версии 3, но считаются устаревшими и, вероятно, будут удалены в какой-то момент в будущем.
-
Замените модификатор слушателя событий
.awayна.outside<!-- 🚫 До --><div x-show="open" @click.away="open = false">...</div><!-- ✅ После --><div x-show="open" @click.outside="open = false">...</div> -
Используйте
Alpine.data()вместо глобальных функций<!-- 🚫 До --><div x-data="dropdown()">...</div><script>function dropdown() {return {...}}</script><!-- ✅ После --><div x-data="dropdown">...</div><script>document.addEventListener('alpine:init', () => {Alpine.data('dropdown', () => ({...}))})</script>