全局状态管理 #

概述 #

在复杂应用中,需要在多个组件之间共享状态。Alpine.js 提供了多种全局状态管理方案,本章将介绍最佳实践。

状态管理策略 #

选择策略 #

场景 推荐方案
简单共享 Alpine.store
模块化状态 多个 Store
复杂逻辑 Store + Actions
类型安全 TypeScript + Store

Alpine.store 进阶 #

模块化 Store #

javascript
document.addEventListener('alpine:init', () => {
    Alpine.store('user', {
        data: null,
        token: localStorage.getItem('token'),
        
        get isLoggedIn() {
            return !!this.token && !!this.data
        },
        
        async login(credentials) {
            const res = await fetch('/api/login', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(credentials)
            })
            const data = await res.json()
            this.token = data.token
            this.data = data.user
            localStorage.setItem('token', data.token)
        },
        
        logout() {
            this.token = null
            this.data = null
            localStorage.removeItem('token')
        }
    })
    
    Alpine.store('cart', {
        items: [],
        
        add(product) {
            const existing = this.items.find(i => i.id === product.id)
            if (existing) {
                existing.quantity++
            } else {
                this.items.push({ ...product, quantity: 1 })
            }
        },
        
        remove(productId) {
            this.items = this.items.filter(i => i.id !== productId)
        },
        
        get total() {
            return this.items.reduce((sum, i) => sum + i.price * i.quantity, 0)
        },
        
        get count() {
            return this.items.reduce((sum, i) => sum + i.quantity, 0)
        },
        
        clear() {
            this.items = []
        }
    })
    
    Alpine.store('notification', {
        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)
            }
        },
        
        remove(id) {
            this.items = this.items.filter(n => n.id !== id)
        },
        
        success(message) { this.add(message, 'success') },
        error(message) { this.add(message, 'error', 5000) },
        warning(message) { this.add(message, 'warning') },
        info(message) { this.add(message, 'info') }
    })
})

Store 持久化 #

javascript
Alpine.store('settings', {
    data: {
        theme: 'light',
        language: 'zh-CN',
        notifications: true
    },
    
    init() {
        this.load()
    },
    
    load() {
        const saved = localStorage.getItem('settings')
        if (saved) {
            this.data = { ...this.data, ...JSON.parse(saved) }
        }
    },
    
    save() {
        localStorage.setItem('settings', JSON.stringify(this.data))
    },
    
    update(key, value) {
        this.data[key] = value
        this.save()
    }
})

状态管理模式 #

Flux 模式 #

javascript
Alpine.store('app', {
    state: {
        user: null,
        posts: [],
        loading: false,
        error: null
    },
    
    actions: {
        async fetchPosts() {
            this.state.loading = true
            this.state.error = null
            try {
                const res = await fetch('/api/posts')
                this.state.posts = await res.json()
            } catch (e) {
                this.state.error = e.message
            } finally {
                this.state.loading = false
            }
        },
        
        async createPost(post) {
            const res = await fetch('/api/posts', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(post)
            })
            const newPost = await res.json()
            this.state.posts.unshift(newPost)
        },
        
        async deletePost(id) {
            await fetch(`/api/posts/${id}`, { method: 'DELETE' })
            this.state.posts = this.state.posts.filter(p => p.id !== id)
        }
    },
    
    get posts() { return this.state.posts },
    get loading() { return this.state.loading },
    get error() { return this.state.error }
})

分层架构 #

javascript
const api = {
    async getPosts() {
        const res = await fetch('/api/posts')
        return res.json()
    },
    async createPost(post) {
        const res = await fetch('/api/posts', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(post)
        })
        return res.json()
    }
}

const postService = {
    posts: [],
    loading: false,
    
    async load() {
        this.loading = true
        this.posts = await api.getPosts()
        this.loading = false
    },
    
    async create(post) {
        const newPost = await api.createPost(post)
        this.posts.unshift(newPost)
    }
}

Alpine.store('posts', postService)

状态同步 #

跨标签页同步 #

javascript
Alpine.store('sync', {
    data: {},
    
    init() {
        this.load()
        window.addEventListener('storage', (e) => {
            if (e.key === 'sync_data') {
                this.data = JSON.parse(e.newValue)
            }
        })
    },
    
    load() {
        const saved = localStorage.getItem('sync_data')
        if (saved) {
            this.data = JSON.parse(saved)
        }
    },
    
    save() {
        localStorage.setItem('sync_data', JSON.stringify(this.data))
    },
    
    set(key, value) {
        this.data[key] = value
        this.save()
    }
})

WebSocket 同步 #

javascript
Alpine.store('realtime', {
    socket: null,
    connected: false,
    data: {},
    
    init() {
        this.connect()
    },
    
    connect() {
        this.socket = new WebSocket('wss://example.com/ws')
        
        this.socket.onopen = () => {
            this.connected = true
        }
        
        this.socket.onclose = () => {
            this.connected = false
            setTimeout(() => this.connect(), 3000)
        },
        
        this.socket.onmessage = (event) => {
            const { type, payload } = JSON.parse(event.data)
            this.handleMessage(type, payload)
        }
    },
    
    handleMessage(type, payload) {
        switch (type) {
            case 'update':
                this.data = { ...this.data, ...payload }
                break
            case 'delete':
                delete this.data[payload.id]
                break
        }
    },
    
    send(type, payload) {
        if (this.connected) {
            this.socket.send(JSON.stringify({ type, payload }))
        }
    }
})

实用示例 #

完整购物车系统 #

javascript
Alpine.store('cart', {
    items: [],
    
    init() {
        this.load()
        this.$watch('items', () => this.save())
    },
    
    load() {
        const saved = localStorage.getItem('cart')
        if (saved) {
            this.items = JSON.parse(saved)
        }
    },
    
    save() {
        localStorage.setItem('cart', JSON.stringify(this.items))
    },
    
    add(product) {
        const existing = this.items.find(i => i.id === product.id)
        if (existing) {
            existing.quantity++
        } else {
            this.items.push({ ...product, quantity: 1 })
        }
        this.notify('added', product)
    },
    
    remove(productId) {
        const item = this.items.find(i => i.id === productId)
        this.items = this.items.filter(i => i.id !== productId)
        this.notify('removed', item)
    },
    
    updateQuantity(productId, quantity) {
        const item = this.items.find(i => i.id === productId)
        if (item) {
            item.quantity = Math.max(0, quantity)
            if (item.quantity === 0) {
                this.remove(productId)
            }
        }
    },
    
    clear() {
        this.items = []
        this.notify('cleared')
    },
    
    get total() {
        return this.items.reduce((sum, i) => sum + i.price * i.quantity, 0)
    },
    
    get count() {
        return this.items.reduce((sum, i) => sum + i.quantity, 0)
    },
    
    notify(event, data) {
        window.dispatchEvent(new CustomEvent(`cart:${event}`, { detail: data }))
    }
})

用户认证系统 #

javascript
Alpine.store('auth', {
    user: null,
    token: null,
    loading: false,
    
    init() {
        this.token = localStorage.getItem('token')
        if (this.token) {
            this.fetchUser()
        }
    },
    
    get isAuthenticated() {
        return !!this.user
    },
    
    async login(credentials) {
        this.loading = true
        try {
            const res = await fetch('/api/login', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(credentials)
            })
            
            if (!res.ok) throw new Error('登录失败')
            
            const data = await res.json()
            this.token = data.token
            this.user = data.user
            localStorage.setItem('token', data.token)
            return true
        } catch (e) {
            console.error(e)
            return false
        } finally {
            this.loading = false
        }
    },
    
    async logout() {
        await fetch('/api/logout', {
            method: 'POST',
            headers: { 'Authorization': `Bearer ${this.token}` }
        })
        this.user = null
        this.token = null
        localStorage.removeItem('token')
    },
    
    async fetchUser() {
        try {
            const res = await fetch('/api/user', {
                headers: { 'Authorization': `Bearer ${this.token}` }
            })
            if (res.ok) {
                this.user = await res.json()
            } else {
                this.logout()
            }
        } catch (e) {
            this.logout()
        }
    }
})

表单状态管理 #

javascript
Alpine.store('form', {
    forms: {},
    
    register(name, initialValues = {}) {
        this.forms[name] = {
            values: { ...initialValues },
            errors: {},
            touched: {},
            dirty: false,
            submitting: false
        }
    },
    
    getForm(name) {
        return this.forms[name]
    },
    
    setValue(formName, field, value) {
        const form = this.forms[formName]
        if (form) {
            form.values[field] = value
            form.dirty = true
        }
    },
    
    setTouched(formName, field) {
        const form = this.forms[formName]
        if (form) {
            form.touched[field] = true
        }
    },
    
    setError(formName, field, error) {
        const form = this.forms[formName]
        if (form) {
            form.errors[field] = error
        }
    },
    
    clearErrors(formName) {
        const form = this.forms[formName]
        if (form) {
            form.errors = {}
        }
    },
    
    reset(formName) {
        const form = this.forms[formName]
        if (form) {
            form.values = {}
            form.errors = {}
            form.touched = {}
            form.dirty = false
            form.submitting = false
        }
    },
    
    async submit(formName, onSubmit) {
        const form = this.forms[formName]
        if (form) {
            form.submitting = true
            try {
                await onSubmit(form.values)
                this.reset(formName)
            } finally {
                form.submitting = false
            }
        }
    }
})

最佳实践 #

1. 单一职责 #

javascript
Alpine.store('user', { /* 用户相关 */ })
Alpine.store('cart', { /* 购物车相关 */ })
Alpine.store('notification', { /* 通知相关 */ })

2. 封装 API 调用 #

javascript
Alpine.store('posts', {
    posts: [],
    
    async fetch() {
        this.posts = await api.getPosts()
    },
    
    async create(post) {
        const newPost = await api.createPost(post)
        this.posts.unshift(newPost)
    }
})

3. 使用 getter #

javascript
Alpine.store('cart', {
    items: [],
    
    get total() {
        return this.items.reduce((sum, i) => sum + i.price, 0)
    },
    
    get isEmpty() {
        return this.items.length === 0
    }
})

4. 错误处理 #

javascript
Alpine.store('data', {
    items: [],
    error: null,
    
    async fetch() {
        try {
            const res = await fetch('/api/data')
            this.items = await res.json()
        } catch (e) {
            this.error = e.message
        }
    }
})

小结 #

全局状态管理要点:

  • 使用 Alpine.store 管理全局状态
  • 按功能模块拆分 Store
  • 封装 API 调用和业务逻辑
  • 使用 getter 封装计算逻辑
  • 实现持久化和同步机制

下一章,我们将学习性能优化。

最后更新:2026-03-28