Alpine.store 全局状态 #
什么是 Alpine.store? #
Alpine.store 是 Alpine.js 提供的全局状态管理方案。它允许你在多个组件之间共享状态,实现跨组件通信。
基本语法 #
注册 Store #
javascript
Alpine.store('storeName', {
property: 'value',
method() {
// ...
}
})
使用 Store #
html
<div x-data>
<span x-text="$store.storeName.property"></span>
</div>
基本用法 #
注册全局状态 #
javascript
document.addEventListener('alpine:init', () => {
Alpine.store('user', {
name: 'John',
email: 'john@example.com',
isLoggedIn: false,
login(name, email) {
this.name = name
this.email = email
this.isLoggedIn = true
},
logout() {
this.isLoggedIn = false
}
})
})
在组件中使用 #
html
<div x-data>
<template x-if="$store.user.isLoggedIn">
<div>
<p>欢迎, <span x-text="$store.user.name"></span></p>
<button @click="$store.user.logout()">退出</button>
</div>
</template>
<template x-if="!$store.user.isLoggedIn">
<button @click="$store.user.login('John', 'john@example.com')">
登录
</button>
</template>
</div>
Store 的响应式 #
Store 中的数据是响应式的:
javascript
Alpine.store('counter', {
count: 0,
increment() {
this.count++
}
})
html
<div x-data>
<span x-text="$store.counter.count"></span>
<button @click="$store.counter.increment()">增加</button>
</div>
<div x-data>
<span x-text="$store.counter.count"></span>
</div>
两个组件显示相同的 count 值,一个组件修改,另一个自动更新。
实用示例 #
主题切换 #
javascript
Alpine.store('theme', {
current: localStorage.getItem('theme') || 'light',
toggle() {
this.current = this.current === 'light' ? 'dark' : 'light'
localStorage.setItem('theme', this.current)
},
isDark() {
return this.current === 'dark'
}
})
html
<body :class="{ 'dark': $store.theme.isDark() }">
<div x-data>
<button @click="$store.theme.toggle()">
<span x-show="!$store.theme.isDark()">🌙 深色模式</span>
<span x-show="$store.theme.isDark()">☀️ 浅色模式</span>
</button>
</div>
</body>
购物车 #
javascript
Alpine.store('cart', {
items: [],
add(product) {
const existing = this.items.find(item => item.id === product.id)
if (existing) {
existing.quantity++
} else {
this.items.push({ ...product, quantity: 1 })
}
},
remove(productId) {
this.items = this.items.filter(item => item.id !== productId)
},
updateQuantity(productId, quantity) {
const item = this.items.find(item => item.id === productId)
if (item) {
item.quantity = Math.max(0, quantity)
if (item.quantity === 0) {
this.remove(productId)
}
}
},
get total() {
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
},
get itemCount() {
return this.items.reduce((sum, item) => sum + item.quantity, 0)
},
clear() {
this.items = []
}
})
html
<div x-data>
<span>购物车 (<span x-text="$store.cart.itemCount"></span>)</span>
</div>
<div x-data>
<template x-for="item in $store.cart.items">
<div>
<span x-text="item.name"></span>
<span x-text="item.quantity"></span>
<button @click="$store.cart.remove(item.id)">删除</button>
</div>
</template>
<p>总计: <span x-text="$store.cart.total"></span></p>
</div>
通知系统 #
javascript
Alpine.store('notifications', {
items: [],
add(message, type = 'info', duration = 3000) {
const id = Date.now()
this.items.push({ id, message, type })
if (duration > 0) {
setTimeout(() => this.remove(id), duration)
}
return id
},
remove(id) {
this.items = this.items.filter(n => n.id !== id)
},
success(message) {
return this.add(message, 'success')
},
error(message) {
return this.add(message, 'error', 5000)
},
warning(message) {
return this.add(message, 'warning')
},
info(message) {
return this.add(message, 'info')
}
})
html
<div x-data class="notifications">
<template x-for="notification in $store.notifications.items">
<div
:class="'notification notification-' + notification.type"
x-text="notification.message"
@click="$store.notifications.remove(notification.id)"
></div>
</template>
</div>
<div x-data>
<button @click="$store.notifications.success('操作成功!')">成功</button>
<button @click="$store.notifications.error('操作失败!')">错误</button>
</div>
用户认证 #
javascript
Alpine.store('auth', {
user: null,
token: localStorage.getItem('token'),
get isAuthenticated() {
return !!this.token && !!this.user
},
async login(credentials) {
const res = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
if (res.ok) {
const data = await res.json()
this.token = data.token
this.user = data.user
localStorage.setItem('token', data.token)
return true
}
return false
},
async logout() {
await fetch('/api/logout', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`
}
})
this.token = null
this.user = null
localStorage.removeItem('token')
},
async fetchUser() {
if (!this.token) return
const res = await fetch('/api/user', {
headers: {
'Authorization': `Bearer ${this.token}`
}
})
if (res.ok) {
this.user = await res.json()
} else {
this.logout()
}
}
})
设置管理 #
javascript
Alpine.store('settings', {
data: {
language: 'zh-CN',
timezone: 'Asia/Shanghai',
notifications: {
email: true,
push: true,
sms: false
}
},
init() {
const saved = localStorage.getItem('settings')
if (saved) {
this.data = { ...this.data, ...JSON.parse(saved) }
}
},
update(key, value) {
if (key.includes('.')) {
const [parent, child] = key.split('.')
this.data[parent][child] = value
} else {
this.data[key] = value
}
this.save()
},
save() {
localStorage.setItem('settings', JSON.stringify(this.data))
}
})
监听 Store 变化 #
使用 $watch #
html
<div x-data x-init="
$watch('$store.counter.count', (value) => {
console.log('count changed:', value)
})
">
</div>
使用 Alpine.effect #
javascript
Alpine.effect(() => {
console.log('count:', Alpine.store('counter').count)
})
Store 初始化 #
使用 init 方法 #
javascript
Alpine.store('app', {
loading: false,
data: null,
async init() {
await this.loadData()
},
async loadData() {
this.loading = true
this.data = await fetch('/api/data').then(r => r.json())
this.loading = false
}
})
使用 alpine:init 事件 #
javascript
document.addEventListener('alpine:init', () => {
Alpine.store('app', {
data: null
})
Alpine.store('app').data = await fetchInitialData()
})
访问 Store #
在模板中 #
html
<span x-text="$store.storeName.property"></span>
<button @click="$store.storeName.method()">调用方法</button>
在 JavaScript 中 #
javascript
Alpine.store('storeName').property
Alpine.store('storeName').method()
在组件方法中 #
html
<div x-data="{
doSomething() {
console.log(this.$store.storeName.property)
}
}">
</div>
最佳实践 #
1. 按功能拆分 Store #
javascript
Alpine.store('user', { /* ... */ })
Alpine.store('cart', { /* ... */ })
Alpine.store('notifications', { /* ... */ })
2. 使用 getter 封装逻辑 #
javascript
Alpine.store('cart', {
items: [],
get total() {
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
},
get isEmpty() {
return this.items.length === 0
}
})
3. 提供清晰的方法 #
javascript
Alpine.store('cart', {
items: [],
add(product) { /* ... */ },
remove(productId) { /* ... */ },
clear() { /* ... */ }
})
4. 持久化关键数据 #
javascript
Alpine.store('user', {
preferences: {},
save() {
localStorage.setItem('preferences', JSON.stringify(this.preferences))
},
load() {
const saved = localStorage.getItem('preferences')
if (saved) {
this.preferences = JSON.parse(saved)
}
}
})
小结 #
Alpine.store 要点:
- 创建全局共享状态
- Store 中的数据是响应式的
- 通过
$store访问 Store - 支持方法和计算属性
- 适合跨组件状态共享
下一章,我们将学习 Alpine.reactive 创建响应式对象。
最后更新:2026-03-28