跳转至

从 V2 升级

以下是 Alpine V3 中破坏性变更的详尽指南,如果你想要更生动的方式,可以通过观看 Alpine Day 2021 的"Alpine 的未来"主题演讲来了解 V3 中的所有变更和新功能:

从 Alpine V2 升级到 V3 应该相当容易。在很多情况下,你的代码库无需做任何修改就能使用 V3。以下是对破坏性变更和弃用内容的详尽列表,按用户可能受影响的程度从高到低排列:

注意:如果你同时使用 Laravel Livewire 和 Alpine,要在 V3 中使用 Alpine,你需要将 Livewire 升级到 v2.5.1 或更高版本。

破坏性变更

$el 现在始终指向当前元素

$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 的自定义魔术属性。

→ 了解更多关于 V3 中的 $el
→ 了解更多关于 V3 中的 $root

自动评估数据对象上定义的 init() 函数

在 V2 中,常见的模式是在 x-data 对象上手动调用 init()(或类似命名的方法)。

在 V3 中,Alpine 会自动调用数据对象上的 init() 方法。

<!-- 🚫 之前 -->
<div x-data="foo()" x-init="init()"></div>

<!-- ✅ 之后 -->
<div x-data="foo()"></div>

<script>
    function foo() {
        return {
            init() {
                //
            }
        }
    }
</script>

→ 了解更多关于自动评估 init 函数

导入后需要调用 Alpine.start()

如果你从 NPM 导入 Alpine V2,现在需要手动调用 Alpine.start() 才能使用 V3。如果你使用 Alpine 的构建文件或通过 <template> 标签使用 CDN,则不受影响。

// 🚫 之前
import 'alpinejs'

// ✅ 之后
import Alpine from 'alpinejs'

window.Alpine = Alpine

Alpine.start()

→ 了解更多关于初始化 Alpine V3

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-transition

x-if 不再支持 x-transition

在 Alpine 中,不再支持在元素从 DOM 中添加/移除之前或之后为其添加过渡效果。

这个功能很少有人知道,更不用说使用了。

由于过渡系统很复杂,从维护角度来看,只支持使用 x-show 的元素进行过渡更为合理。

<!-- 🚫 之前 -->
<template x-if.transition="open">
    <div>...</div>
</template>

<!-- ✅ 之后 -->
<div x-show="open" x-transition>...</div>

→ 了解更多关于 x-if

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-data 作用域

x-init 不再接受回调返回值

在 V3 之前,如果 x-init 接收到的返回值 typeof 为 "function",Alpine 会在完成初始化树中所有其他指令后执行该回调。现在,你需要手动调用 $nextTick() 来实现相同的行为。x-init 不再"感知返回值"。

<!-- 🚫 之前 -->
<div x-data x-init="() => { ... }">...</div>

<!-- ✅ 之后 -->
<div x-data x-init="$nextTick(() => { ... })">...</div>

→ 了解更多关于 $nextTick

从事件处理器返回 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-on

x-spread 现在是 x-bind

Alpine 复用功能的方式之一是将 Alpine 指令抽象为对象,然后通过 x-spread 应用于元素。这种行为保持不变,只是现在 x-bind(不指定属性)替代了 x-spread 成为新的 API。

<!-- 🚫 之前 -->
<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>

→ 了解更多关于使用 x-bind 绑定指令

使用全局生命周期事件替代 Alpine.deferLoadingAlpine()

<!-- 🚫 之前 -->
<script>
    window.deferLoadingAlpine = startAlpine => {
        // 将在初始化 Alpine 之前执行。

        startAlpine()

        // 将在初始化 Alpine 之后执行。
    }
</script>

<!-- ✅ 之后 -->
<script>
    document.addEventListener('alpine:init', () => {
        // 将在初始化 Alpine 之前执行。
    })

    document.addEventListener('alpine:initialized', () => {
        // 将在初始化 Alpine 之后执行。
    })
</script>

→ 了解更多关于 Alpine 生命周期事件

x-ref 不再支持动态绑定

在 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,因此只有第一个 ref 会按预期返回。

不再支持 IE11

Alpine 将不再正式支持 Internet Explorer 11。如果你需要支持 IE11,建议仍使用 Alpine V2。

弃用的 API

以下 2 个 API 在 V3 中仍然可用,但已被标记为弃用,并可能在将来某个时候被移除。

事件监听器修饰符 .away 应替换为 .outside

<!-- 🚫 之前 -->
<div x-show="open" @click.away="open = false">
    ...
</div>

<!-- ✅ 之后 -->
<div x-show="open" @click.outside="open = false">
    ...
</div>

优先使用 Alpine.data() 而非全局 Alpine 函数数据提供者

<!-- 🚫 之前 -->
<div x-data="dropdown()">
    ...
</div>

<script>
    function dropdown() {
        return {
            ...
        }
    }
</script>

<!-- ✅ 之后 -->
<div x-data="dropdown">
    ...
</div>

<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('dropdown', () => ({
            ...
        }))
    })
</script>

注意,你需要在调用 Alpine.start() 之前定义 Alpine.data() 扩展。更多信息请参考生命周期问题作为模块安装文档页面。