该文档介绍了在项目中如何使用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.data
将 x-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(数据绑定-核心指令)
创建从组件数据到元素属性的单向数据绑定。它支持单个属性绑定和基于对象的多个属性绑定。( :
简写)
核心源码:
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 元素上。
核心源码:
事件处理系统通过 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) |
这些可以处理像 click
、 auxclick
、 contextmenu
、 mouseenter
、 mouseleave
等事件。
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>
元素上,并且该模板必须包含且仅包含一个根元素。
核心源码:
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
进行过渡切换。
核心源码:
通过 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-init
或 x-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 元素。它最常用于替代 getElementById
和 querySelector
这类 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 元素。
核心源码:
指令的实现遵循由 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
的替代方案。
注意事项:
- 避免滥用
$refs
,如果可以用数据绑定(x-model
)实现,优先用数据驱动的方式。 $refs
在init()
之后才可用,如果在x-init
里使用,确保组件已挂载。- 在 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
)避免与浏览器原生事件冲突。 - 性能优化:高频事件(如鼠标移动)建议用
debounce
或throttle
修饰符
$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>
动态计算元素尺寸
如果不使用 $nextTick
,offsetWidth
可能获取到的是旧值。
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>