Skip to content

参考

基于 Alpine.js 3.x 编写 —— 15 个指令 / 9 个魔术属性 / 3 个全局 API / 9 个官方插件 / 高级 API / TypeScript / 版本里程碑

15 个指令清单

指令用途必须 <template>
x-data声明响应式作用域
x-init初始化逻辑
x-show切换显示(display: none
x-bind / :绑定 HTML 属性
x-on / @监听事件
x-text渲染文本
x-html渲染 HTML(XSS 风险)
x-model双向绑定
x-modelable让自定义组件支持 x-model
x-for列表渲染
x-transition进出过渡(配 x-show
x-effect响应式副作用
x-ignore跳过 Alpine 处理
x-ref引用 DOM 元素
x-cloak隐藏未初始化内容
x-teleportDOM 传送(配 modal)
x-if条件渲染(DOM 增删)
x-id唯一 ID 作用域

注:清单合计 18 项(含 x-if / x-teleport / x-modelable),官方称「~15 directives」是按概念归类。

x-data

html
x-data="objectOrName"
  • { key: value }:内联对象字面量
  • componentName:引用 Alpine.data(name, ...) 注册的组件
  • componentName(arg):传参

x-init

html
x-init="expression"
  • 元素首次被 Alpine 处理时执行
  • 在第一次 DOM 更新前执行
  • init() 方法会先于 x-init 执行

x-show

html
x-show="condition"
x-show.important="condition"  <!-- 加 !important -->

x-bind

html
x-bind:attr="value"
:attr="value"
x-bind="objectOrName"  <!-- 批量绑定 -->

x-on

html
x-on:event="expression"
@event="expression"
@event.modifier="expression"
@event.key="expression"

x-text / x-html

html
x-text="expression"
x-html="expression"  <!-- 仅受信任内容 -->

x-model

html
x-model="property"
x-model.lazy="property"
x-model.number="property"
x-model.boolean="property"
x-model.debounce.500ms="property"
x-model.throttle.500ms="property"
x-model.fill="property"

x-modelable

html
<div x-data="{ count: 0 }" x-modelable="count">
  <!-- 暴露 count 给外部 x-model -->
</div>

x-for

html
<template x-for="item in items" :key="item.id">
  <li x-text="item.name"></li>
</template>

<template x-for="(item, index) in items">...</template>
<template x-for="(value, key) in object">...</template>
<template x-for="i in 10">...</template>

x-transition

html
x-transition
x-transition.duration.500ms
x-transition.delay.50ms
x-transition.opacity
x-transition.scale.80
x-transition.scale.origin.top.right
x-transition:enter.duration.500ms
x-transition:leave.duration.300ms

<!-- 自定义类 -->
x-transition:enter="..."
x-transition:enter-start="..."
x-transition:enter-end="..."
x-transition:leave="..."
x-transition:leave-start="..."
x-transition:leave-end="..."

x-effect

html
x-effect="expression"
  • 自动追踪表达式中读到的响应式属性
  • 首次 + 任意依赖变化时执行
  • 类似 Vue 的 watchEffect

x-ignore

html
<div x-ignore>...</div>  <!-- 跳过 Alpine 处理 -->

x-ref + $refs

html
<input x-ref="email">
<button @click="$refs.email.focus()">Focus</button>

x-cloak

html
<div x-cloak x-data="...">...</div>
css
[x-cloak] { display: none !important; }

x-teleport

html
<template x-teleport="body">
  <!-- 传送到 body 末尾 -->
</template>

<template x-teleport="#modal-root">...</template>

x-if

html
<template x-if="condition">
  <div>Only one root element</div>
</template>

x-id

html
<div x-id="['my-id', 'another']">
  <label :for="$id('my-id')">...</label>
  <input :id="$id('my-id')">
</div>

9 个魔术属性清单

Magic用途
$el当前 DOM 元素
$refsx-ref 集合
$store全局 store(Alpine.store
$watch监听属性变化
$dispatch派发自定义事件
$nextTick等 DOM 更新完
$root最近 x-data 根元素
$data当前 reactive scope 整体
$id生成唯一 ID

$el

html
<button @click="$el.classList.add('clicked')">Add class</button>

$refs

html
<div x-data>
  <input x-ref="input">
  <button @click="$refs.input.focus()">Focus</button>
</div>

$store

html
<!-- 任意 reactive scope 访问 -->
<span x-text="$store.user.name"></span>
<button @click="$store.cart.add(item)">Add</button>

$watch

js
this.$watch('search', (value, oldValue) => { ... })
this.$watch('user.profile.name', value => { ... })  // 嵌套

$dispatch

js
$dispatch('event-name')
$dispatch('event-name', { detail: 'data' })
$dispatch('event-name', payload, { bubbles: true, cancelable: true })

$nextTick

js
// 回调形式
$nextTick(() => { ... })

// Promise 形式
await $nextTick()

$root

html
<div x-data data-message="Hi">
  <div>
    <button @click="alert($root.dataset.message)">Show</button>
  </div>
</div>

$data

js
// 把当前整个 reactive scope 传出去
externalFunc($data)

$id

html
<div x-id="['my-id']">
  <label :for="$id('my-id')">...</label>
  <input :id="$id('my-id')">
</div>

<!-- 带 key(loop 中) -->
<div x-id="['list-item']">
  <template x-for="(item, i) in items">
    <li :id="$id('list-item', [i])"></li>
  </template>
</div>

3 个全局 API

Alpine.data(name, factory)

js
Alpine.data('dropdown', () => ({
  open: false,
  init() { /* 自动调用 */ },
  destroy() { /* 自动调用 */ },
  toggle() { this.open = !this.open },
}))

// 带参数
Alpine.data('counter', (start = 0) => ({
  count: start,
  increment() { this.count++ },
}))

Alpine.store(name, data)

js
// 对象 store
Alpine.store('cart', {
  items: [],
  init() { /* 自动调用 */ },
  add(item) { this.items.push(item) },
  get total() { return this.items.length },
})

// 单值 store
Alpine.store('darkMode', false)

Alpine.bind(name, attributes)

js
Alpine.bind('PrimaryButton', () => ({
  type: 'button',
  class: 'btn-primary',
  '@click'() { this.handleClick() },
  ':disabled'() { return this.loading },
}))
html
<button x-data x-bind="PrimaryButton">...</button>

9 个官方插件清单

插件npm 包主要 API
Persist@alpinejs/persist$persist(value).as(key).using(storage)
Intersect@alpinejs/intersectx-intersect / :enter / :leave
Mask@alpinejs/maskx-mask / x-mask:dynamic / $money
Morph@alpinejs/morphAlpine.morph(el, html, opts) / Alpine.morphBetween
Focus@alpinejs/focusx-trap / $focus
Collapse@alpinejs/collapsex-collapse.duration.500ms / .min.50px
Anchor@alpinejs/anchorx-anchor / Floating UI 内核
Sort@alpinejs/sortx-sort / :item / :handle / :group
Resize@alpinejs/resizex-resize / $width / $height / .document

Persist 完整 API

js
$persist(initialValue)                     // localStorage
$persist(initialValue).as('custom-key')    // 自定义 key
$persist(initialValue).using(sessionStorage)  // sessionStorage
$persist(initialValue).using(customStorage)   // 自定义(需 getItem / setItem)

Alpine.$persist(...)                       // 全局访问(store / Alpine.data 外)

Intersect 修饰符

修饰符作用
.once只触发一次
.half50% 阈值
.full99% 阈值
.threshold.XX自定义阈值 0-100
.margin.XXpx视口边距
:enter / :leave仅进入 / 仅离开

Mask 占位符

占位符匹配
9数字(0-9)
a字母(a-z, A-Z)
*任意字符
9{n}n 个数字
a{n}n 个字母
html
<input x-mask="999-9999">
<input x-mask:dynamic="condition ? 'a' : 'b'">
<input x-mask:dynamic="$money($input)">

Morph 钩子

js
Alpine.morph(el, newHtml, {
  key(el) { return el.id },
  updating(from, to, childrenOnly, skip) { },
  updated(from, to) { },
  removing(el, skip) { },
  removed(el) { },
  adding(el, skip) { },
  added(el) { },
})

Focus 修饰符 / Magic

html
<div x-trap="open">                          <!-- 基础 -->
<div x-trap.inert="open">                    <!-- + aria-hidden 兄弟节点 -->
<div x-trap.noscroll="open">                 <!-- 锁滚动 -->
<div x-trap.noreturn="open">                 <!-- 不还原焦点 -->
<div x-trap.noautofocus="open">              <!-- 不自动 focus -->
js
$focus.focus(el)            // 聚焦指定
$focus.first()              // 首个可聚焦
$focus.last()               // 末个
$focus.next()               // 下一个
$focus.previous()           // 上一个
$focus.wrap()               // 启用循环
$focus.within(el)           // 限定容器
$focus.focusable(el)        // 检查
$focus.focusables()         // 全部

Anchor 位置 + 修饰符

html
<div x-anchor="$refs.button">                          <!-- 默认 bottom -->
<div x-anchor.bottom-start="...">
<div x-anchor.bottom-end="...">
<div x-anchor.top="...">
<div x-anchor.top-start="...">
<div x-anchor.top-end="...">
<div x-anchor.left="...">
<div x-anchor.left-start="...">
<div x-anchor.left-end="...">
<div x-anchor.right="...">
<div x-anchor.right-start="...">
<div x-anchor.right-end="...">

<div x-anchor.offset.10="...">                <!-- 偏移 -->
<div x-anchor.noflip="...">                   <!-- 不自动翻转 -->
<div x-anchor.no-style="..." :style="$anchor">  <!-- 自己控制样式 -->

Sort 配置

html
<ul x-sort>...</ul>
<ul x-sort="(key, position) => move(key, position)">...</ul>
<ul x-sort.ghost>                              <!-- 视觉占位 -->
<ul x-sort x-sort:group="my-group">            <!-- 跨容器拖 -->

<!-- 子元素 -->
<li x-sort:item="key">
  <span x-sort:handle>⋮⋮</span>             <!-- 仅 handle 可拖 -->
  <button x-sort:ignore>Delete</button>       <!-- 不触发拖拽 -->
</li>

<!-- 透传 SortableJS 配置 -->
<ul x-sort x-sort:config="{ animation: 300 }">...</ul>

Resize 用法

html
<div x-resize="width = $width; height = $height">...</div>
<div x-resize.document="...">              <!-- 整个 document -->

高级 API

Alpine.reactive(obj) / Alpine.effect(fn) / Alpine.release(dispose)

js
const data = Alpine.reactive({ count: 0 })

const dispose = Alpine.effect(() => {
  console.log(data.count)
})

data.count++   // 触发 effect

Alpine.release(dispose)  // 停止订阅

Alpine.directive(name, callback)

js
Alpine.directive('uppercase', (el, { expression }, { evaluateLater, effect }) => {
  const getter = evaluateLater(expression)
  effect(() => {
    getter(value => {
      el.textContent = String(value).toUpperCase()
    })
  })
})

Alpine.magic(name, callback)

js
Alpine.magic('clipboard', () => subject => navigator.clipboard.writeText(subject))
html
<button @click="$clipboard('Hello!')">Copy</button>

Alpine.plugin(plugin)

js
import persist from '@alpinejs/persist'
import intersect from '@alpinejs/intersect'

Alpine.plugin(persist)
Alpine.plugin(intersect)
// 或批量
Alpine.plugin([persist, intersect])

Alpine.start() / Alpine.initTree(el)

js
Alpine.start()              // 启动 + 扫描整个 document
Alpine.initTree(element)    // 仅扫描指定子树(动态插入新 DOM 时用)

全局生命周期事件

js
// 启动前(注册 store / data / plugin 的时机)
document.addEventListener('alpine:init', () => {
  Alpine.data('foo', ...)
})

// 启动完成后
document.addEventListener('alpine:initialized', () => {
  console.log('Alpine ready')
})

CDN 链接(推荐锁版本)

html
<!-- 核心 -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>

<!-- CSP 友好版(替代核心,二选一) -->
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/csp@3.14.1/dist/cdn.min.js"></script>

<!-- 插件(在核心之前加载) -->
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.14.1/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/intersect@3.14.1/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/mask@3.14.1/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/morph@3.14.1/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/focus@3.14.1/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/collapse@3.14.1/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/anchor@3.14.1/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/sort@3.14.1/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/resize@3.14.1/dist/cdn.min.js"></script>

NPM 包

用途
alpinejs核心
@alpinejs/cspCSP 友好版
@alpinejs/persist$persist 状态持久化
@alpinejs/intersectx-intersect 视口检测
@alpinejs/maskx-mask 输入掩码
@alpinejs/morphAlpine.morph DOM 差量更新
@alpinejs/focusx-trap 焦点管理
@alpinejs/collapsex-collapse 折叠动画
@alpinejs/anchorx-anchor 浮动定位
@alpinejs/sortx-sort 拖拽排序
@alpinejs/resizex-resize 尺寸监听

TypeScript 配置

bash
pnpm add alpinejs
pnpm add -D @types/alpinejs
ts
// src/main.ts
import Alpine from 'alpinejs'
import persist from '@alpinejs/persist'

declare global {
  interface Window {
    Alpine: typeof Alpine
  }
}

window.Alpine = Alpine
Alpine.plugin(persist)

interface CartItem {
  id: number
  price: number
}

Alpine.data('cart', () => ({
  items: [] as CartItem[],
  add(item: CartItem) { this.items.push(item) },
}))

Alpine.start()

Alpine 内部属性(DevTools 调试用)

属性含义
el._x_dataStack元素的 reactive scope 栈(最近 x-data)
el._x_attributesAddedAlpine 添加的属性
el._x_ignore是否被 x-ignore 标记
el._x_modelx-model 绑定的 getter/setter
el._x_refs$refs 引用集合
js
// DevTools 选中元素后:
$0._x_dataStack[0]    // 当前 scope
Alpine.$data($0)      // 同义(API)

版本里程碑

版本时间关键特性
1.02019.11Caleb Porzio 首次发布;10 个指令
2.02020.7$store 引入;性能优化
2.82021.42.x 收官
3.02021.5重写 ESM 架构;新增魔术属性 / 插件系统
3.42021.10x-modelable / x-teleport
3.102022.5x-effect / Morph 改进
3.122023.2x-id 引入;性能提升
3.132023.10Sort 插件 / Anchor 插件 / Mask 改进
3.142024.1Morph 算法 / x-bind 性能提升

推荐版本

3.14.x 是当前主流稳定版。CDN 锁版用 @3.14.1;新项目用 @3.x.x(自动取最新 3.x)。

浏览器兼容

浏览器支持
Chrome / Edge84+
Firefox75+
Safari13+
IE 11不支持(Alpine 3 用 ES2020+)

资源

相关项目

  • Livewire(laravel-livewire/livewire):Caleb Porzio 的 Laravel 全栈框架,深度集成 Alpine
  • Pines(pinesui/pines):基于 Alpine + Tailwind 的免费组件集合
  • Penguin UI(penguinui/penguin-ui):Alpine 组件库
  • Component Party(component-party.dev):Alpine 与其他框架对比的代码片段集合