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