Alpine.js 技术文档

229 阅读15分钟

该文档介绍了在项目中如何使用Alpine,主要是把实际开发中用到的知识点总结一下,再加上一些区别于官网文档的扩展,如果需要了解更完整的官方文档,请直接访问官网文档 Alpine.js 是一个轻量级的 JavaScript 框架(压缩后约 15kB),它允许你使用声明式指令为你的 HTML 添加交互行为。它提供了类似 Vue.js 等大型框架的响应式和声明式特性,但体积更小,专注于渐进式增强现有的 HTML。

由于轻量级的设计,Alpine.js 不依赖虚拟 DOM,而是直接遍历真实 DOM 进行更新,在大型应用中可能性能较差,但其轻量级特性使其在简单交互场景中表现优异,适合小型项目或渐进式增强。

Alpine.js与Vue的语法很接近,在响应式实现方式上是使用的Proxy来监听数据变化(类似 Vue 3 ),除非在不支持Proxy的旧浏览器上,它会退回使用Object.defineProperty(类似 Vue 2 ),如果有vue开发基础的话上手起来会非常快。

一、Directives(指令)

Alpine.js 包含多个核心指令,为其提供功能:

1. x-data

当页面加载时,Alpine.js 会扫描 DOM 中带有 x-data 指令的元素,并将它们初始化为 Alpine 组件。随后,它会设置响应式系统并应用所有其他指令。此外,它还使用 MutationObserver 来监视初始化后 DOM 中添加的新元素。

这是一个虚构的下拉组件示例:

<div x-data="{ open: false }">
    <button x-on:click="open = ! open">Toggle Content</button>
 
    <div x-show="open">
        Content...
    </div>
</div>

如果你发现自己重复 x-data 的内容,或者发现内联语法冗长,你可以使用 Alpine.datax-data 对象提取到一个专门的组件中。

<div x-data="dropdown">
    <button x-on:click="toggle">Toggle Content</button>
 
    <div x-show="open">
        Content...
    </div>
</div>
 
<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('dropdown', () => ({
            open: false,
 
            toggle() {
                this.open = ! this.open
            },
        }))
    })
</script>

2. x-init

该指令允许你钩入 Alpine 中任何元素的初始化阶段,类似vue的mounted或者react的useEffect的空依赖数组

<div
    x-data="{ posts: [] }"
    x-init="posts = await (await fetch('/posts')).json()"
>...</div>

init也可以写到x-data中,而且写在x-data中的init,会在x-init之前执行。

<div
    x-data="{
        init() {
            console.log('I am called first')
        }
    }"
    x-init="console.log('I am called second')"
    >
    ...
</div>
<div x-data="dropdown">
     ...
</div>
 
<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('dropdown', () => ({
            init() {
                console.log('I will get evaluated when initializing each "dropdown" component.')
            },
        }))
    })
</script>

3. x-show

本质上就是根据表达式切换 display: none

<div x-data="{ open: false }">
    <button x-on:click="open = ! open">Toggle Dropdown</button>
 
    <div x-show="open">
        Dropdown Contents...
    </div>
</div>

可以使用 .important 修饰符将行内样式设置为 display: none !important

<div x-data="{ open: false }">
    <button x-on:click="open = ! open">Toggle Dropdown</button>
 
    <div x-show.important="open">
        Dropdown Contents...
    </div>
</div>

如果您想对 x-show 行为应用平滑过渡,可以使用它与 x-transition 结合使用。

<div x-data="{ open: false }">
    <button x-on:click="open = ! open">Toggle Dropdown</button>
 
    <div x-show="open" x-transition>
        Dropdown Contents...
    </div>
</div>

4. x-bind(数据绑定-核心指令)

创建从组件数据到元素属性的单向数据绑定。它支持单个属性绑定和基于对象的多个属性绑定。( : 简写)

核心源码:

github.com/alpinejs/al…

Basic Usage 基本用法

<div x-data="{ message: 'Hello' }">
    <!-- Full syntax -->
    <span x-bind:title="message">Hover me</span>

    <!-- Shorthand syntax -->
    <span :title="message">Hover me</span>
</div>
<div x-bind:class="show ? '' : 'hidden'">
<div :class="show ? '' : 'hidden'">
<div x-bind:style="{ color: 'red', display: 'flex' }">
<div :style="{ color: 'red', display: 'flex' }">

Object Binding Syntax 对象绑定语法

x-bind 允许你将包含不同指令和属性的多个对象绑定到元素上。

<div x-bind="{ class: 'active', 'data-id': userId }">
<div x-data="dropdown">
    <button x-bind="trigger">Open Dropdown</button>
    <span x-bind="dialogue">Dropdown Contents</span>
</div>
 
<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('dropdown', () => ({
            open: false,
            trigger: {
                ['x-ref']: 'trigger',
                ['@click']() {
                    this.open = true
                },
            },
 
            dialogue: {
                ['x-show']() {
                    return this.open
                },
                ['@click.outside']() {
                    this.open = false
                },
            },
        }))
    })
</script>

5. x-model(数据绑定-核心指令)

在表单元素和组件数据之间创建双向数据绑定。当用户与表单元素交互时,组件数据会自动更新,反之亦然。

核心源码:

github.com/alpinejs/al… github.com/alpinejs/al…

Basic Usage 基本用法

<!-- x-model 是双向绑定的,这意味着它既能“设置”也能“获取”。除了改变数据外,如果数据本身发生变化,元素也会反映这一变化。-->
<div x-data="{ message: '' }">
    <input type="text" x-model="message">
    <button x-on:click="message = 'changed'">Change Message</button>
</div>

x-model 可以与以下输入元素配合使用:

  • <input type="text">
  • <textarea>
  • <input type="checkbox">
  • <input type="radio">
  • <select>
  • <input type="range">

Modifiers 修饰符

x-model 指令支持多个修饰符,这些修饰符可以改变其行为:

Modifier 修饰符Description 描述
.numberAutomatically type-casts the value as a number 自动将值转换为数字
.booleanAutomatically type-casts the value as a boolean 自动将值转换为布尔值
.trimTrims whitespace from the value 从值中删除空白
.lazyUpdates on change instead of input events (less frequent) 在值变化时更新而不是输入事件(频率较低)
.fillSets the initial value from the element's default value 从元素默认值设置初始值
.unintrusiveDoesn't modify input values when focused 聚焦时不修改输入值

与 x-modelable 集成

对于高级场景,您可以使用 x-modelable 将任何属性暴露为 x-model 的目标:

<div x-data="{ name: 'John' }">
    <div x-data="{ value: '' }" x-modelable="value" x-model="name">
        <!-- Custom input implementation -->
        <button @click="value = 'Jane'">Set to Jane</button>
    </div>
</div>

这将在内部的 value 属性和外部 name 属性之间创建双向绑定。

6. x-on(事件处理-核心指令)

通过 x-on 指令或其简写 @ 符号将事件监听器附加到 DOM 元素上。

核心源码:

github.com/alpinejs/al…

事件处理系统通过 on() 函数实现,该函数以中间件风格处理事件。 wrapHandler() 函数创建一个包装函数链,在最终执行 callback() 之前应用修饰符。

Basic Usage 基本用法

附加事件监听器( @ 简写)

<button x-on:click="alert('Hello World!')">Say Hi</button>
<button @click="alert('Hello World!')">Say Hi</button>
<button @click="count++; showMessage = true">Increment </ button > 

Event Modifiers 事件修饰符

Alpine 提供了多种指令修饰符来定制你的事件监听器的行为。

Action Modifiers 动作修饰符
Modifier 修饰符Description 描述
.preventCalls event.preventDefault() 调用 event.preventDefault()
.stopCalls event.stopPropagation() 调用 event.stopPropagation()
.selfOnly triggers if event target is the element itself 仅当事件目标本身是元素时才触发
<!-- 阻止了浏览器默认行为(如提交、跳转)-->
<form @submit.prevent="console.log('submitted')" action="/foo">
    <button>Submit</button>
</form>

<!-- 阻止了事件的冒泡或捕获 -->
<div @click="console.log('I will not get logged')">
    <button @click.stop>Click Me</button>
</div>

<!-- 只有点击按钮本身才会调用 handleClick -->
<button @click.self="handleClick">
    Click Me
    <img src="...">
</button>
Target Modifiers 目标修饰符
Modifier 修饰符Description 描述
.windowAttaches listener to the window object 将监听器附加到窗口对象
.documentAttaches listener to the document object 将监听器附加到文档对象
.outsideTriggers when event occurs outside the element 当事件在元素外部发生时触发 (also available as .away)
<!-- 监听页面上的任何位置按下的"escape"键 -->
<div @keyup.escape.window="...">...</div>

<!-- .document 与 .window 类似,只是它在 document 全局上注册监听器,而不是 window 全局 -->
<div @keyup.escape.document="...">...</div>

<!-- 通过点击内容外的任何位置来关闭下拉菜单 -->
<div x-data="{ open: false }">
    <button @click="open = ! open">Toggle</button>
 
    <div x-show="open" @click.outside="open = false">
        Contents...
    </div>
</div>
Execution Modifiers 执行修饰符
Modifier 修饰符Description 描述
.onceHandler executes only once, then detaches 处理器仅执行一次,然后解绑
.passiveSets passive: true in the listener options 在监听器选项中设置 passive: true
.captureTriggers in the capturing phase instead of bubble 在捕获阶段触发,而不是冒泡
<!-- 通过给监听器添加 .once ,确保了处理程序只被调用一次。 -->
<button @click.once="console.log('I will only log once')">...</button>

<!-- 添加 .passive 到你的监听器中很重要,以避免阻塞滚动性能。-->
<div @touchstart.passive="...">...</div>

<!-- 如果你想在事件捕获阶段执行这个监听器,例如在事件从目标元素冒泡到 DOM 之前,请添加这个修饰符。-->
<div @click.capture="console.log('I will log first')">
    <button @click="console.log('I will log second')"></button>
</div>
Timing Modifiers 计时修饰符
Modifier 修饰符Description 描述
.debounceDelays execution until after a pause in events 延迟执行,直到事件暂停后(防抖)
.throttleLimits execution to once per specified interval 限制执行为每指定间隔一次(节流)
<!-- 通过在 .debounce 修饰符后添加持续时间来实现延长或缩短防抖时间,默认是250ms -->
<input @input.debounce="fetchResults">
<input @input.debounce.500ms="fetchResults">

<!-- 通过添加 .throttle ,确保 handleScroll 每隔 250 毫秒只被调用一次 -->
<div @scroll.window.throttle="handleScroll">...</div>
<div @scroll.window.throttle.750ms="handleScroll">...</div>
Event Name Modifiers 事件名称修饰符
Modifier 修饰符Description 描述
.camelConverts kebab-case event names to camelCase 将 kebab-case 事件名称转换为 camelCase
.dotConverts dashes to dots in event names 将事件名称中的连字符转换为点
<!-- 通过添加 .camel,Alpine 现在监听的是 customEvent 而不是 custom-event -->
<div @custom-event.camel="handleCustomEvent">
    ...
</div>

<!-- custom-event.dot 将对应事件名称 custom.event -->
<div @custom-event.dot="handleCustomEvent">
    ...
</div>
Keyboard Event Handling 键盘事件处理

Alpine 为键盘事件提供了带键修饰符的特殊支持:

<input type="text" @keyup.enter="submitForm()">

您可以使用键修饰符与这些事件一起使用:

  • keydown
  • keyup

Common key modifiers include:

常见的键修饰符包括:

Modifier 修饰符Key 键
.enterEnter key
.spaceSpace key
.escapeEscape key
.upUp arrow
.downDown arrow
.leftLeft arrow
.rightRight arrow
.tabTab key
.shiftShift key
.ctrlControl key
.altAlt key
.meta/.cmdCmd/Windows key

You can also chain modifiers for key combinations:

您也可以为键组合链式添加修饰符:

<input @keydown.ctrl.s.prevent="saveDocument()">
Mouse Event Modifiers 鼠标事件修饰符

类似于键盘事件,鼠标事件也可以有修饰符:

<button @click.shift="addToSelection()">Item</button>

支持鼠标事件的系统键修饰符有:

Modifier 修饰符Event Property 事件属性
.shiftshiftKey
.ctrlctrlKey
.altaltKey
.metametaKey(这两个修饰符在处理时会被统一转换为 meta)
.cmdmetaKey(这两个修饰符在处理时会被统一转换为 meta)

这些可以处理像 clickauxclickcontextmenumouseentermouseleave 等事件。

Custom Events 自定义事件

Alpine 事件监听器可以响应任何 DOM 事件,包括自定义事件:

<div x-data @custom-event="alert('Custom event fired!')">
    <button @click="$dispatch('custom-event')">Fire Custom Event</button>
</div>

$dispatch 辅助工具简化了创建和派发具有冒泡功能的自定义事件。

如果你的自定义事件名称包含驼峰式命名或点,请使用相应的修饰符:

<div @my-custom-event.camel="..."> <!-- Listens for 'myCustomEvent' --> </div>
<div @custom-event-name.dot="..."> <!-- Listens for 'custom.event.name' --> </div>

7. x-text

用于将元素的文本内容设置为给定表达式的结果。当属性中的值更新时,他会更新元素的 innerText

<div x-data="{ username: 'calebporzio' }">
    Username: <strong x-text="username"></strong>
</div>

8. x-html

将元素的 "innerHTML" 属性设置为给定表达式的结果,当属性中的值更新时,他会更新元素的 innerHTML

⚠️ 只能用在可信内容上,绝不要直接展示用户的内容 ⚠️

动态渲染第三方来源的 HTML 会非常容易造成 XSS 漏洞

<div x-data="{ username: '<strong>calebporzio</strong>' }">
    Username: <span x-html="username"></span>
</div>

9. x-for(控制流-核心指令)

x-for 指令处理列表渲染和迭代。允许你通过遍历列表来创建 DOM 元素。它必须用于 <template> 元素上,并且该模板必须包含且仅包含一个根元素。

核心源码:

github.com/alpinejs/al…

Basic Usage 基本用法

在使用 x-for 时,请记住:

  • It must be declared on a <template> element 它必须在 <template> 元素上声明
  • The template must contain only one root element 模板必须只包含一个根元素
  • You must include a parent element with x-data defined 你必须包含一个定义了 x-data 的父元素
<template x-for="item in items">
    <div> <!-- Content using item --> </div>
</template>

当列表中的项目可能被重新排序、添加或删除时,您应该使用 :key 属性来帮助 Alpine 高效地跟踪和更新元素:

注意⚠️:不建议使用index当作key使用

<template x-for="color in colors" :key="color.id">
    <li x-text="color.label"></li>
</template>

Expression Parsing 表达式解析

The parseForExpression function parses various iteration syntaxes: parseForExpression 函数解析多种迭代语法:

Syntax Pattern 语法模式Parsed Components 解析组件Usage 使用
item in items{ item, items }Basic iteration 基础遍历
(item, index) in items{ item, index, items }With index access 带索引访问
(item, index, collection) in items{ item, index, collection, items }With collection reference 带集合引用
[a, b] in itemsArray destructuring 数组解构Destructure array items 解构数组项
{key, value} in itemsObject destructuring 对象解构Destructure object items 解构对象项

10. x-if(控制流-核心指令)

x-if 用于在页面上切换元素,类似于 x-show ,但它会完全添加和移除它所应用的元素,而不是仅仅将它的 CSS display 属性更改为 "none"。而且与 x-show 不同, x-if 不支持使用 x-transition 进行过渡切换。

核心源码:

github.com/alpinejs/al…

通过 show()hide() 函数提供条件渲染,这些函数管理元素的生命周期。

Basic Usage 基本用法

在使用 x-if 时,请记住:

  • It must be declared on a <template> element 它必须在 <template> 元素上声明
  • The template must contain only one root element 模板必须只包含一个根元素
  • You must include a parent element with x-data defined 你必须包含一个定义了 x-data 的父元素
<template x-if="open">
    <div>Contents...</div>
</template>

11. x-transition

在元素显示或隐藏时创建平滑的过渡效果。

在 Alpine 中处理过渡主要有两种方式:

The Transition Helper 过渡辅助工具

使用 Alpine 实现过渡效果最简单的方法是将 x-transition 添加到带有 x-show 的元素上。例如:

<div x-data="{ open: false }">
    <button @click="open = ! open">Toggle</button>
 
    <div x-show="open" x-transition>
        Hello 👋
    </div>
</div>

可以通过附加到 x-transition 的修饰符来覆盖动画的默认值。

<!-- 自定义持续时间 -->
<div ... x-transition.duration.500ms>

<!-- 自定义延迟 -->
<div ... x-transition.delay.50ms>

<!-- 如果您只想应用透明度过渡(不缩放) -->
<div ... x-transition.opacity>

<!-- 如果您只想应用缩放过渡(不过渡透明度) -->
<div ... x-transition.scale>

<!-- 元素放大和缩小 80%  -->
<div ... x-transition.scale.80>

<!-- 分别自定义进入和离开过渡的值 -->
<div ...
    x-transition:enter.scale.80
    x-transition:leave.scale.90
>
<!-- 自定义缩放过渡的原点 -->
<div ... x-transition.scale.origin.top>

Applying CSS Classes 应用 CSS 类

为了直接控制过渡中具体使用的内容,你可以在过渡的不同阶段应用 CSS 类。

<div x-data="{ open: false }">
    <button @click="open = ! open">Toggle</button>
 
    <div
        x-show="open"
        x-transition:enter="transition ease-out duration-300"
        x-transition:enter-start="opacity-0 scale-90"
        x-transition:enter-end="opacity-100 scale-100"
        x-transition:leave="transition ease-in duration-300"
        x-transition:leave-start="opacity-100 scale-100"
        x-transition:leave-end="opacity-0 scale-90"
    >Hello 👋</div>
</div>
Directive 指令Description 描述
:enterApplied during the entire entering phase. 在整个进入阶段应用。
:enter-startAdded before element is inserted, removed one frame after element is inserted. 在元素插入之前添加,在元素插入后移除一帧。
:enter-endAdded one frame after element is inserted (at the same time enter-start is removed), removed when transition/animation finishes. 在元素插入后一帧添加(与 enter-start 移除同时发生),在过渡/动画完成时移除。
:leaveApplied during the entire leaving phase. 在整个离开阶段应用。
:leave-startAdded immediately when a leaving transition is triggered, removed after one frame. 当离开过渡被触发时立即添加,一帧后移除。
:leave-endAdded one frame after a leaving transition is triggered (at the same time leave-start is removed), removed when the transition/animation finishes. 在离开过渡被触发后添加一帧(同时移除 leave-start ),在过渡/动画完成时移除。

12. x-effect

主要用于监听 基本类型(如字符串、数字、布尔值) 或 简单响应式变量 的变化,并在其依赖项更新时执行回调函数。

<div x-data="{ label: 'Hello' }" x-effect="console.log(label)">
    <button @click="label += ' World!'">Change Message</button>
</div>

然而,x-effect默认使用 JavaScript 的严格相等 (===) 来检测变化, 默认不支持深度监听对象内部属性的变化,这意味着如果对象的某个属性被修改,但对象引用本身未变,x-effect 可能不会触发。如果想要监听对象内部属性的变化,可以用以下的几个方案

  • 使用 Magics中的$watch 监听特定属性
<div x-data="{ user: { name: 'John', age: 30 } }" 
     x-init="$watch('user', value => console.log(value))"></div>
  • 手动触发更新(如重新赋值对象)
this.user = { ...this.user, name: 'New Name' };
  • 使用 JSON.stringify()
<div x-data="{ user: { name: 'John', age: 30 } }">
    <div x-effect="JSON.stringify(user)"></div>
</div>
  • 监听特定属性
<div x-data="{ user: { name: 'John', age: 30 } }">
    <div x-effect="$el.textContent = user.name"></div>
</div>
  1. x-ignore

默认情况下,Alpine 会遍历并初始化包含 x-initx-data 的元素的全部 DOM 树。

如果出于某种原因,您不希望 Alpine 触及 HTML 的特定部分,您可以使用 x-ignore 来阻止它这样做。

<div x-data="{ label: 'From Alpine' }">
    <div x-ignore>
        <span x-text="label"></span>
    </div>
</div>

在上面的示例中, <span> 标签不会显示 "From Alpine",因为我们告诉 Alpine 完全忽略 div 的内容。

14. x-ref

x-ref$refs 结合使用是一个有用的工具,可以轻松直接访问 DOM 元素。它最常用于替代 getElementByIdquerySelector 这类 API。

<div x-data={...}>
    <button @click="$refs.text.remove()">Remove Text</button>
    <span x-ref="text">Hello 👋</span>
</div>    

15. x-cloak

有时,当你使用 AlpineJS 处理模板的一部分时,可能会出现一个“闪烁”的情况,即在页面加载后、Alpine 加载之前,你会看到未初始化的模板。

x-cloak 通过隐藏其附加的元素来解决这个问题,直到页面上的 Alpine 完全加载。

<!-- 在其 x-show 特性被显式设置为 true 之前隐藏 <span> 标签
防止 Alpine 加载时隐藏的元素出现在屏幕上。 -->
<span x-cloak x-show="false">This will not 'blip' onto screen at any point</span>

<!-- x-cloak 不仅适用于通过 x-show 或 x-if 隐藏的元素
它还确保包含数据的元素在数据正确设置之前保持隐藏。
在 Alpine 将其文本内容设置为 message 属性之前隐藏 <span> 标签。 -->
<span x-cloak x-text="message"></span>

然而,要让 x-cloak 正常工作,你必须将以下 CSS 添加到页面中。

[x-cloak] { display: none !important; }

如果您想实现相同的行为,但避免包含全局样式,您可以使用以下这个酷炫但诚然有些古怪的小技巧:

<template x-if="true">
    <span x-text="message"></span>
</template>

16. x-teleport(DOM操作-核心指令)

该指令能够将模板内容移动到 DOM 的不同位置,同时保持其原始的 Alpine 作用域和响应性。该指令提供了一种突破正常 DOM 层次结构的机制,特别适用于模态对话框、工具提示和其他需要摆脱父容器样式约束的 UI 元素。

核心源码:

github.com/alpinejs/al…

指令的实现遵循由 directive() 函数注册管理的特定生命周期。当 Alpine 遇到具有 x-teleport 属性的 <template> 元素时,则会开始进行移动。

Basic Usage 基本用法

x-teleport 选择器可以是任何你通常传递给 document.querySelector 的字符串。它会找到第一个匹配的元素,无论是标签名( body )、类名( .my-class )、ID( #my-id ),或其他任何有效的 CSS 选择器。

<body>
    <div x-data="{ open: false }">
        <button @click="open = ! open">Toggle Modal</button>
 
        <template x-teleport="body">
            <div x-show="open">
                Modal contents...
            </div>
        </template>
    </div>
 
    <div>Some other content placed AFTER the modal markup.</div>
 
    ...
 
</body>

上面的示例中,当切换模态时,发现实际的模态内容显示在“Some other content...”元素之后,这是因为当 Alpine 初始化时,它看到 x-teleport="body" 并将初始化该元素到提供的元素选择器。

Placement Modifiers 放置修饰符

指令支持三种放置模式,由修饰符控制:

Modifier 修饰符Behavior 行为Implementation 实现
Default 默认target.appendChild(clone)Appends to target's children 追加到目标元素的子元素中
.prependtarget.parentNode.insertBefore(clone, target)Inserts before target 插入到目标元素之前
.appendtarget.parentNode.insertBefore(clone, target.nextSibling)Inserts after target 在目标后插入
 <!-- Prepend before target -->
<template x-teleport.prepend="#container">
    <div>Prepended content</div>
</template>

<!-- Append after target -->
<template x-teleport.append="#container">
    <div>Appended content</div>
</template>

Event Forwarding 事件转发

在模板元素上注册的事件会自动从传送内容中转发。

<template x-teleport="body" @click="closeModal">
    <div x-show="open" @click.stop>
        Modal content (click outside to close)
    </div>
</template>

Nested Teleportation 嵌套传送

指令支持嵌套传送场景,传送的内容可以包含自己的传送指令,这种模式对于需要生成额外模态框的模态对话框特别有用。

<template x-teleport="body">
    <div x-show="outerModal">
        <template x-teleport="body">
            <div x-show="innerModal">Nested modal</div>
        </template>
    </div>
</template>

17. x-id

x-id 允许你为使用 $id() 生成的任何新 ID 声明一个新的 "作用域"。它接受一个字符串数组(ID 名称),并为其中生成的每个 $id('...') 添加一个独特的后缀,以区别于页面上的其他 ID。

⚠️注意:x-id 指令生成的 ID 本质上确实是基于递增的索引(index)进行拼接的,而不是纯粹的随机数。它的设计目的是生成稳定且可预测的唯一 ID,而不是完全随机的字符串。所以在某些x-for循环中不建议把$id()当成key使用。

x-id 是与 Magics中的$id 一起使用的。

<div x-id="['text-input']">
    <label :for="$id('text-input')">Username</label>
    <!-- for="text-input-1" -->
 
    <input type="text" :id="$id('text-input')">
    <!-- id="text-input-1" -->
</div>
 
<div x-id="['text-input']">
    <label :for="$id('text-input')">Username</label>
    <!-- for="text-input-2" -->
 
    <input type="text" :id="$id('text-input')">
    <!-- id="text-input-2" -->
</div>

二、Magics(魔术属性)

1. $el

$el 可用于获取当前的 DOM 节点。

类似于 Vue 中的 this.$el 或 jQuery 中的 $(this)

<button @click="$el.innerHTML = 'Hello World!'">Replace me with "Hello World!"</button>

2. $refs

用于获取组件内部使用 x-ref 标记的 DOM 元素。

通常被用作更简洁、作用域更明确的 document.querySelector 的替代方案。

注意事项:

  1. 避免滥用 $refs,如果可以用数据绑定(x-model)实现,优先用数据驱动的方式。
  2. $refsinit() 之后才可用,如果在 x-init 里使用,确保组件已挂载。
  3. 在 V3 中, $refs 只能被访问到静态创建的元素
<button @click="$refs.text.remove()">Remove Text</button>
 
<span x-ref="text">Hello 👋</span>

$refs$el的区别:

特性$el$refs(通过 x-ref 定义)
指向当前组件的根 DOM 元素手动标记的特定子元素
获取方式直接使用 this.$el通过 this.$refs.refName
用途操作整个组件 DOM精确访问某个子元素
<div x-data="{
  focusInput() {
    this.$el.querySelector('input').focus(); // 使用 $el + querySelector
    // 或
    this.$refs.myInput.focus(); // 使用 $refs
  }
}">
    <input x-ref="myInput" type="text">
    <button @click="focusInput">聚焦输入框</button>
</div>

3. $store

$store 是 Alpine.js 提供的全局状态管理工具,允许你在不同组件之间共享数据,而无需通过 $parent 或事件层层传递。

$store 的作用:集中管理全局状态(类似 Vue 的 Pinia/Vuex 或 React 的 Context/Redux)。

注意⚠️:避免滥用,仅将真正需要全局共享的数据放入 Store。

您可以使用 $store 方便地访问使用 Alpine.store(...) 注册的全局 Alpine store。


Alpine.store('cart', {
  items: [],
  addItem(product) {
    this.items.push(product);
  },
  clearCart() {
    this.items = [];
  }
});
<button @click="$store.cart.addItem({ id: 1, name: '商品A' })">添加到购物车</button>

最佳实践:

  • 避免直接修改 Store
 // 不推荐
$store.user.name = 'Bob';
// 推荐
Alpine.store('user', {setName(name) {this.name = name;}});
  • 结合 $watch 监听变化
Alpine.store('settings', {
    theme: 'light',
    init() {
        // 监听 theme 变化
        this.$watch('theme', (newVal) => {
          console.log('主题切换为:', newVal);
        });
    }
});
  • 持久化存储

配合 localStorage 实现状态持久化:

Alpine.store('auth', {
    token: localStorage.getItem('token') || '',
    setToken(token) {
        this.token = token;
        localStorage.setItem('token', token);
    }
});

4. $watch

$watch 是 Alpine.js 提供的响应式监听工具,用于在数据变化时执行回调函数(类似 Vue 的 watch 或 React 的 useEffect)。

监听 x-data$store 中的数据变化,并在变化时触发自定义逻辑。

Basic Usage 基本用法

<div x-data="{ count: 0 }" 
    x-init="$watch('count', (newVal, oldVal) => {
      console.log(`count 从 ${oldVal} 变为 ${newVal}`);
    })">
    <button @click="count++">+1</button>
</div>

或者

<div x-data="dropdown">
...
</div>
 
<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('dropdown', () => ({
            count: 0,
            init(){
                this.$watch('count', (newVal, oldVal) => {
                  console.log(`count 从 ${oldVal} 变为 ${newVal}`);
                })
            }
        }))
    })
</script>

Deep watching 深度监视

$watch 会自动监视任何级别的变化,但您应该记住,当检测到变化时,监视器将返回被观察属性的值,而不是已更改的子属性的值。

<div x-data="{ foo: { bar: 'baz' }}" 
    x-init="$watch('foo', (value, oldValue) => console.log(value, oldValue))">
    <button @click="foo.bar = 'bob'">Update</button>
</div>

⚠️不要将被"监控"对象的属性在 $watch 回调中进行修改,这将产生无限循环,并最终导致错误。

<!-- 🚫 Infinite loop -->
<div x-data="{ foo: { bar: 'baz', bob: 'lob' }}" 
    x-init="$watch('foo', value => foo.bob = foo.bar)">
    <button @click="foo.bar = 'bob'">Update</button>
</div>

5. $dispatch

$dispatch 是 Alpine.js 提供的自定义事件派发机制,允许组件触发事件并在父组件或全局监听该事件(类似于浏览器的原生 CustomEvent,但更集成化)。有点类似EventBus的概念。

$dispatch 的事件会冒泡(类似浏览器原生事件),因此可以在任意父级监听

<div @notify="alert($event.detail.message)">
    <button @click="$dispatch('notify', { message: 'Hello World!' })">
        Notify
    </button>
</div>

<!-- 通过 .stop 修饰符阻止事件继续冒泡,
点击后只会触发按钮的 $dispatch,不会触发外层的 @parent-event -->
<div @parent-event="console.log('父组件')">
    <div x-data>
        <button @click.stop="$dispatch('parent-event')">不冒泡</button>
    </div>
</div>

由于事件冒泡的机制,当你需要捕获来自同一嵌套层级节点的派发事件时,你需要使用 .window 修饰符

<!-- 🚫 Won't work -->
<div x-data>
    <span @notify="..."></span>
    <button @click="$dispatch('notify')">Notify</button>
</div>
 
<!-- ✅ Will work (because of .window) -->
<div x-data>
    <span @notify.window="..."></span>
    <button @click="$dispatch('notify')">Notify</button>
</div>

你也可以利用之前的技术让你的组件相互通信

<div
    x-data="{ title: 'Hello' }"
    @set-title.window="title = $event.detail"
>
    <h1 x-text="title"></h1>
</div>
 
<div x-data>
    <button @click="$dispatch('set-title', 'Hello World!')">Click me</button>
</div>
<!-- When clicked, the content of the h1 will set to "Hello World!". -->

注意事项

  • 事件名约定:推荐使用 kebab-case(如 form-submit)避免与浏览器原生事件冲突。
  • 性能优化:高频事件(如鼠标移动)建议用 debouncethrottle修饰符

6. $nextTick

$nextTick 是 Alpine.js 提供的异步工具,用于在 DOM 更新后 执行代码,确保你访问的是最新的 DOM 状态(类似于 Vue 的 nextTick)。

典型场景:

  • 在修改数据后立即操作 DOM(如聚焦输入框、计算元素尺寸)。
  • 避免因 Alpine 的异步更新机制导致的 DOM 状态不同步问题。

Basic Usage 基本用法

修改数据后操作 DOM

如果直接调用 $refs.myInput.focus(),由于 x-if 的 DOM 更新是异步的,输入框可能尚未渲染,导致报错。用 $nextTick 确保 DOM 更新完成后再聚焦。

<div x-data="{ showInput: false }">
  <button @click="showInput = true; $nextTick(() => $refs.myInput.focus())">
    显示并聚焦输入框
  </button>

  <template x-if="showInput">
    <input x-ref="myInput" type="text">
  </template>
</div>
动态计算元素尺寸

如果不使用 $nextTickoffsetWidth 可能获取到的是旧值。

<div x-data="{ width: 0 }">
  <div x-ref="box" :style="{ padding: '20px' }">动态宽度元素</div>
  
  <button @click="
    $refs.box.style.width = '200px';
    $nextTick(() => { width = $refs.box.offsetWidth });
  ">
    计算宽度
  </button>

  <div>实际宽度: <span x-text="width"></span>px</div>
</div>
第三方库初始化

在 DOM 渲染完成后初始化依赖 DOM 的库(如图表库)

<div x-data="{
  init() {
      this.$nextTick(() => {
        new Chart(this.$refs.chart, { /* 配置 */ });
      });
  }
}">
  <canvas x-ref="chart"></canvas>
</div>

7. $root

$root 是一个魔法属性,可用于获取任何 Alpine 组件的根元素。换句话说,它是 DOM 树中包含 x-data 的最接近的元素。

<div x-data data-message="Hello World!">
    <button @click="alert($root.dataset.message)">Say Hi</button>
</div>
<div x-data data-message="1">  <!-- 根组件 -->
  <div x-data data-message="2">  <!-- 子组件 -->
    <div x-data data-message="3">  <!-- 孙子组件 -->
      <span x-text="$root.dataset.message"></span> <!-- 显示"3" -->
    </div>
  </div>
</div>

在Alpine.js的较新版本中,也可以通过_x_dataStack直接访问数据对象

<div x-data="{ message: 'Hello World!' }">
    <button @click="alert($root._x_dataStack[0].message)">Say Hi</button>
</div>

8. $data

它用于获取访问当前的 Alpine 作用域所有数据的对象

<div x-data="{ greeting: 'Hello' }">
    <div x-data="{ name: 'Caleb' }">
        <button @click="sayHello($data)">Say Hello</button>
    </div>
</div>
 
<script>
    function sayHello({ greeting, name }) {
        alert(greeting + ' ' + name + '!')
    }
</script>

9. $id

可用于生成元素的 ID,并确保它不会与同一页面上其他同名 ID 冲突。

<input type="text" :id="$id('text-input')">
<!-- id="text-input-1" -->
 
<input type="text" :id="$id('text-input')">
<!-- id="text-input-2" -->

也可以设置第二个参数

<ul
    x-id="['list-item']"
    :aria-activedescendant="$id('list-item', activeItem.id)"
>
    <template x-for="item in items" :key="item.id">
        <li :id="$id('list-item', item.id)">...</li>
    </template>
</ul>