Alpine.js 技术文档

该文档介绍了在项目中如何使用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 包含多个核心指令,为其提供功能:

x-data

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

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

ini 复制代码
<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 对象提取到一个专门的组件中。

xml 复制代码
<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>

x-init

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

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

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

ini 复制代码
<div
    x-data="{
        init() {
            console.log('I am called first')
        }
    }"
    x-init="console.log('I am called second')"
    >
    ...
</div>
xml 复制代码
<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>

x-show

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

ini 复制代码
<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

ini 复制代码
<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 结合使用。

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

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

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

核心源码:

github.com/alpinejs/al...

Basic Usage 基本用法

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

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

Object Binding Syntax 对象绑定语法

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

css 复制代码
<div x-bind="{ class: 'active', 'data-id': userId }">
xml 复制代码
<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>

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

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

核心源码:

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

Basic Usage 基本用法

xml 复制代码
<!-- 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 描述
.number Automatically type-casts the value as a number 自动将值转换为数字
.boolean Automatically type-casts the value as a boolean 自动将值转换为布尔值
.trim Trims whitespace from the value 从值中删除空白
.lazy Updates on change instead of input events (less frequent) 在值变化时更新而不是输入事件(频率较低)
.fill Sets the initial value from the element's default value 从元素默认值设置初始值
.unintrusive Doesn't modify input values when focused 聚焦时不修改输入值

与 x-modelable 集成

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

xml 复制代码
<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 属性之间创建双向绑定。

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

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

核心源码:

github.com/alpinejs/al...

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

Basic Usage 基本用法

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

ini 复制代码
<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 描述
.prevent Calls event.preventDefault() 调用 event.preventDefault()
.stop Calls event.stopPropagation() 调用 event.stopPropagation()
.self Only triggers if event target is the element itself 仅当事件目标本身是元素时才触发
xml 复制代码
<!-- 阻止了浏览器默认行为(如提交、跳转)-->
<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 描述
.window Attaches listener to the window object 将监听器附加到窗口对象
.document Attaches listener to the document object 将监听器附加到文档对象
.outside Triggers when event occurs outside the element 当事件在元素外部发生时触发 (also available as .away)
xml 复制代码
<!-- 监听页面上的任何位置按下的"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 描述
.once Handler executes only once, then detaches 处理器仅执行一次,然后解绑
.passive Sets passive: true in the listener options 在监听器选项中设置 passive: true
.capture Triggers in the capturing phase instead of bubble 在捕获阶段触发,而不是冒泡
xml 复制代码
<!-- 通过给监听器添加 .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 描述
.debounce Delays execution until after a pause in events 延迟执行,直到事件暂停后(防抖)
.throttle Limits execution to once per specified interval 限制执行为每指定间隔一次(节流)
xml 复制代码
<!-- 通过在 .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 描述
.camel Converts kebab-case event names to camelCase 将 kebab-case 事件名称转换为 camelCase
.dot Converts dashes to dots in event names 将事件名称中的连字符转换为点
xml 复制代码
<!-- 通过添加 .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 为键盘事件提供了带键修饰符的特殊支持:

ini 复制代码
<input type="text" @keyup.enter="submitForm()">

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

  • keydown
  • keyup

Common key modifiers include:

常见的键修饰符包括:

Modifier 修饰符 Key 键
.enter Enter key
.space Space key
.escape Escape key
.up Up arrow
.down Down arrow
.left Left arrow
.right Right arrow
.tab Tab key
.shift Shift key
.ctrl Control key
.alt Alt key
.meta/.cmd Cmd/Windows key

You can also chain modifiers for key combinations:

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

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

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

ini 复制代码
<button @click.shift="addToSelection()">Item</button>

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

Modifier 修饰符 Event Property 事件属性
.shift shiftKey
.ctrl ctrlKey
.alt altKey
.meta metaKey(这两个修饰符在处理时会被统一转换为 meta)
.cmd metaKey(这两个修饰符在处理时会被统一转换为 meta)

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

Custom Events 自定义事件

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

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

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

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

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

x-text

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

ini 复制代码
<div x-data="{ username: 'calebporzio' }">
    Username: <strong x-text="username"></strong>
</div>

x-html

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

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

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

ini 复制代码
<div x-data="{ username: '<strong>calebporzio</strong>' }">
    Username: <span x-html="username"></span>
</div>

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 的父元素
xml 复制代码
<template x-for="item in items">
    <div> <!-- Content using item --> </div>
</template>

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

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

ini 复制代码
<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 items Array destructuring 数组解构 Destructure array items 解构数组项
{key, value} in items Object destructuring 对象解构 Destructure object items 解构对象项
复制代码
### 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 的父元素
xml 复制代码
<template x-if="open">
    <div>Contents...</div>
</template>
复制代码
### x-transition

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

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

The Transition Helper 过渡辅助工具

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

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

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

xml 复制代码
<!-- 自定义持续时间 -->
<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 类。

ini 复制代码
<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 描述
:enter Applied during the entire entering phase. 在整个进入阶段应用。
:enter-start Added before element is inserted, removed one frame after element is inserted. 在元素插入之前添加,在元素插入后移除一帧。
:enter-end Added one frame after element is inserted (at the same time enter-start is removed), removed when transition/animation finishes. 在元素插入后一帧添加(与 enter-start 移除同时发生),在过渡/动画完成时移除。
:leave Applied during the entire leaving phase. 在整个离开阶段应用。
:leave-start Added immediately when a leaving transition is triggered, removed after one frame. 当离开过渡被触发时立即添加,一帧后移除。
:leave-end Added one frame after a leaving transition is triggered (at the same time leave-start is removed), removed when the transition/animation finishes. 在离开过渡被触发后添加一帧(同时移除 leave-start ),在过渡/动画完成时移除。
复制代码
### x-effect

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

css 复制代码
<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 监听特定属性
ini 复制代码
<div x-data="{ user: { name: 'John', age: 30 } }" 
     x-init="$watch('user', value => console.log(value))"></div>
  • 手动触发更新(如重新赋值对象)
kotlin 复制代码
this.user = { ...this.user, name: 'New Name' };
  • 使用 JSON.stringify()
css 复制代码
<div x-data="{ user: { name: 'John', age: 30 } }">
    <div x-effect="JSON.stringify(user)"></div>
</div>
  • 监听特定属性
css 复制代码
<div x-data="{ user: { name: 'John', age: 30 } }">
    <div x-effect="$el.textContent = user.name"></div>
</div>
复制代码
### x-ignore

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

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

css 复制代码
<div x-data="{ label: 'From Alpine' }">
    <div x-ignore>
        <span x-text="label"></span>
    </div>
</div>

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

复制代码
### x-ref

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

xml 复制代码
<div x-data={...}>
    <button @click="$refs.text.remove()">Remove Text</button>
    <span x-ref="text">Hello 👋</span>
</div>    
复制代码
### x-cloak

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

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

xml 复制代码
<!-- 在其 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 添加到页面中。

css 复制代码
[x-cloak] { display: none !important; }

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

ini 复制代码
<template x-if="true">
    <span x-text="message"></span>
</template>
复制代码
### 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 选择器。

xml 复制代码
<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 追加到目标元素的子元素中
.prepend target.parentNode.insertBefore(clone, target) Inserts before target 插入到目标元素之前
.append target.parentNode.insertBefore(clone, target.nextSibling) Inserts after target 在目标后插入
xml 复制代码
 <!-- 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 事件转发

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

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

Nested Teleportation 嵌套传送

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

ini 复制代码
<template x-teleport="body">
    <div x-show="outerModal">
        <template x-teleport="body">
            <div x-show="innerModal">Nested modal</div>
        </template>
    </div>
</template>
复制代码
### x-id

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

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

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

xml 复制代码
<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(魔术属性)

$el

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

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

dart 复制代码
<button @click="$el.innerHTML = 'Hello World!'">Replace me with "Hello World!"</button>

$refs

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

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

注意事项:

  1. 避免滥用 $refs,如果可以用数据绑定(x-model)实现,优先用数据驱动的方式。
  2. $refsinit() 之后才可用,如果在 x-init 里使用,确保组件已挂载。
  3. 在 V3 中, $refs 只能被访问到静态创建的元素
ini 复制代码
<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 精确访问某个子元素
bash 复制代码
<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>

$store

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

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

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

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

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

最佳实践:

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

配合 localStorage 实现状态持久化:

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

$watch

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

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

Basic Usage 基本用法

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

或者

xml 复制代码
<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 会自动监视任何级别的变化,但您应该记住,当检测到变化时,监视器将返回被观察属性的值,而不是已更改的子属性的值。

ini 复制代码
<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 回调中进行修改,这将产生无限循环,并最终导致错误。

xml 复制代码
<!-- 🚫 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>

$dispatch

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

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

xml 复制代码
<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 修饰符

xml 复制代码
<!-- 🚫 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>

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

xml 复制代码
<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修饰符

$nextTick

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

典型场景:

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

Basic Usage 基本用法

修改数据后操作 DOM

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

ini 复制代码
<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 可能获取到的是旧值。

xml 复制代码
<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 的库(如图表库)

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

$root

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

ini 复制代码
<div x-data data-message="Hello World!">
    <button @click="alert($root.dataset.message)">Say Hi</button>
</div>
xml 复制代码
<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直接访问数据对象

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

$data

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

xml 复制代码
<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>

$id

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

xml 复制代码
<input type="text" :id="$id('text-input')">
<!-- id="text-input-1" -->
 
<input type="text" :id="$id('text-input')">
<!-- id="text-input-2" -->

也可以设置第二个参数

ini 复制代码
<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>
相关推荐
彭于晏爱编程5 小时前
Vite 打包超 500KB 警告优化实录
前端
飞翔的佩奇5 小时前
【完整源码+数据集+部署教程】【天线&运输】直升机战机类型识别目标检测系统源码&数据集全套:改进yolo11-CSP-EDLAN
前端·python·yolo·计算机视觉·数据集·yolo11·直升机战机类型识别目标检测系统
JA+6 小时前
vue 实时数据表格组件 (stk-table-vue)
前端·javascript·vue.js
那年窗外下的雪.6 小时前
鸿蒙ArkUI布局与样式进阶(十二)——自定义TabBar + class类机制全解析(含手机商城底部导航案例)
开发语言·前端·javascript·华为·智能手机·harmonyos·arkui
IT_陈寒7 小时前
Python性能优化:5个被低估但效果惊人的内置函数实战解析
前端·人工智能·后端
00后程序员张7 小时前
Fiddler使用教程,全面掌握Fiddler抓包工具的配置方法、代理设置与调试技巧(HTTPHTTPS全解析)
前端·测试工具·ios·小程序·fiddler·uni-app·webview
前端架构师-老李7 小时前
15、Electron专题:使用 electron-store 进行本地数据存储
前端·javascript·electron
小白学大数据7 小时前
双管齐下:结合显式等待与Timeout处理复杂Ajax网页
前端·javascript·ajax