UniApp跨端状态管理深度解析:Pinia+Vue3实战企业级应用架构 | 移动开发教程

2026-04-21 0 673

引言:为什么UniApp需要现代化的状态管理?

随着UniApp生态的成熟和Vue3的普及,传统的Vuex状态管理方案在复杂跨端应用中逐渐暴露出诸多问题:

  • TypeScript支持不够完善
  • 模块化组织复杂
  • 组合式API集成不够优雅
  • 开发体验和调试体验有待提升

Pinia作为Vue官方推荐的状态管理库,完美解决了这些问题。本文将深入探讨如何在UniApp项目中集成Pinia,构建可维护、高性能的企业级应用架构。

第一部分:UniApp + Vue3 + Pinia 环境搭建

1.1 创建UniApp项目

# 使用Vue3模板创建UniApp项目
npx degit dcloudio/uni-preset-vue#vite my-uniapp-project

# 进入项目目录
cd my-uniapp-project

# 安装依赖
npm install

# 安装Pinia及相关依赖
npm install pinia @pinia/native-plugin

1.2 项目结构配置

my-uniapp-project/
├── src/
│   ├── stores/           # Pinia状态管理
│   │   ├── modules/      # 业务模块store
│   │   ├── index.ts      # store入口文件
│   │   └── types/        # 类型定义
│   ├── composables/      # 组合式函数
│   ├── pages/           # 页面文件
│   ├── static/          # 静态资源
│   └── main.ts          # 应用入口
├── uni.scss            # 全局样式
├── manifest.json       # 应用配置
└── package.json

1.3 初始化Pinia配置

// src/stores/index.ts
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate'

// 创建Pinia实例
const pinia = createPinia()

// 添加持久化插件(针对不同平台的适配)
pinia.use(createPersistedState({
    storage: {
        getItem(key: string): string | null {
            // 跨端存储适配
            #ifdef H5
            return localStorage.getItem(key)
            #endif
            
            #ifdef MP-WEIXIN
            return uni.getStorageSync(key)
            #endif
            
            #ifdef APP-PLUS
            return plus.storage.getItem(key)
            #endif
        },
        setItem(key: string, value: string) {
            #ifdef H5
            localStorage.setItem(key, value)
            #endif
            
            #ifdef MP-WEIXIN
            uni.setStorageSync(key, value)
            #endif
            
            #ifdef APP-PLUS
            plus.storage.setItem(key, value)
            #endif
        }
    }
}))

export default pinia

第二部分:Pinia核心概念与基础使用

2.1 创建第一个Store

// src/stores/modules/user.ts
import { defineStore } from 'pinia'
import type { UserInfo, LoginParams } from '../types/user'

export const useUserStore = defineStore('user', {
    state: () => ({
        token: '',
        userInfo: null as UserInfo | null,
        isLoggedIn: false,
        loginLoading: false
    }),
    
    getters: {
        // 计算属性
        userName: (state) => state.userInfo?.name || '未登录',
        userId: (state) => state.userInfo?.id || '',
        
        // 带参数的计算属性
        hasPermission: (state) => (permission: string) => {
            return state.userInfo?.permissions?.includes(permission) || false
        }
    },
    
    actions: {
        // 异步action
        async login(params: LoginParams) {
            this.loginLoading = true
            try {
                const response = await uni.request({
                    url: '/api/user/login',
                    method: 'POST',
                    data: params
                })
                
                this.token = response.data.token
                this.userInfo = response.data.userInfo
                this.isLoggedIn = true
                
                // 登录成功后跳转
                uni.switchTab({
                    url: '/pages/home/index'
                })
            } catch (error) {
                uni.showToast({
                    title: '登录失败',
                    icon: 'error'
                })
                throw error
            } finally {
                this.loginLoading = false
            }
        },
        
        // 同步action
        logout() {
            this.$reset() // 重置state
            uni.removeStorageSync('token')
            uni.reLaunch({
                url: '/pages/login/index'
            })
        },
        
        // 更新用户信息
        updateUserInfo(info: Partial) {
            if (this.userInfo) {
                this.userInfo = { ...this.userInfo, ...info }
            }
        }
    },
    
    // 持久化配置
    persist: {
        key: 'user-store',
        paths: ['token', 'userInfo', 'isLoggedIn']
    }
})

2.2 在组件中使用Store

<template>
    <view class="user-profile">
        <view v-if="userStore.isLoggedIn">
            <image :src="userStore.userInfo?.avatar" class="avatar" />
            <text class="name">{{ userStore.userName }}</text>
            <text class="welcome">欢迎回来!</text>
            
            <button @click="handleLogout" :loading="userStore.loginLoading">
                退出登录
            </button>
        </view>
        
        <view v-else>
            <button @click="goToLogin">立即登录</button>
        </view>
    </view>
</template>

<script setup lang="ts">
import { useUserStore } from '@/stores/modules/user'
import { storeToRefs } from 'pinia'

const userStore = useUserStore()

// 使用storeToRefs保持响应性
const { userName, isLoggedIn } = storeToRefs(userStore)

const handleLogout = () => {
    uni.showModal({
        title: '提示',
        content: '确定要退出登录吗?',
        success: (res) => {
            if (res.confirm) {
                userStore.logout()
            }
        }
    })
}

const goToLogin = () => {
    uni.navigateTo({
        url: '/pages/login/index'
    })
}
</script>

第三部分:高级状态管理技巧

3.1 模块化Store组织

// src/stores/modules/cart.ts - 购物车模块
export const useCartStore = defineStore('cart', {
    state: () => ({
        items: [] as CartItem[],
        selectedIds: [] as string[],
        totalPrice: 0
    }),
    
    getters: {
        itemCount: (state) => state.items.length,
        selectedCount: (state) => state.selectedIds.length,
        selectedItems: (state) => {
            return state.items.filter(item => 
                state.selectedIds.includes(item.id)
            )
        }
    },
    
    actions: {
        // 与其他store交互
        async checkout() {
            const userStore = useUserStore()
            if (!userStore.isLoggedIn) {
                uni.navigateTo({ url: '/pages/login/index' })
                return
            }
            
            // 结算逻辑
            const orderStore = useOrderStore()
            await orderStore.createOrder(this.selectedItems)
            
            // 清空已选商品
            this.clearSelected()
        }
    }
})

// src/stores/modules/order.ts - 订单模块
export const useOrderStore = defineStore('order', {
    // 订单相关状态管理
})

3.2 组合式Store模式

// src/stores/composables/usePagination.ts
import { ref, computed } from 'vue'

export function usePagination(store: any, fetchAction: string) {
    const currentPage = ref(1)
    const pageSize = ref(10)
    const loading = ref(false)
    
    const total = computed(() => store.total || 0)
    const totalPages = computed(() => 
        Math.ceil(total.value / pageSize.value)
    )
    
    const loadData = async (page = 1) => {
        if (loading.value) return
        
        loading.value = true
        try {
            await store[fetchAction]({
                page,
                pageSize: pageSize.value
            })
            currentPage.value = page
        } finally {
            loading.value = false
        }
    }
    
    const nextPage = () => {
        if (currentPage.value  {
        if (currentPage.value > 1) {
            loadData(currentPage.value - 1)
        }
    }
    
    return {
        currentPage,
        pageSize,
        loading,
        total,
        totalPages,
        loadData,
        nextPage,
        prevPage
    }
}

// 在Store中使用
export const useProductStore = defineStore('product', {
    state: () => ({
        products: [],
        total: 0
    }),
    
    actions: {
        async fetchProducts(params: PaginationParams) {
            const response = await uni.request({
                url: '/api/products',
                data: params
            })
            this.products = response.data.list
            this.total = response.data.total
        }
    }
})

// 在组件中使用
const productStore = useProductStore()
const pagination = usePagination(productStore, 'fetchProducts')

// 初始化加载
onMounted(() => {
    pagination.loadData(1)
})

第四部分:性能优化与最佳实践

4.1 状态持久化策略

// src/stores/plugins/storage.ts
import { PiniaPluginContext } from 'pinia'

export function createStoragePlugin() {
    return (context: PiniaPluginContext) => {
        const { store, options } = context
        
        // 自动清理过期数据
        if (options.persist?.expires) {
            const savedTime = uni.getStorageSync(`${store.$id}-saved-time`)
            if (savedTime && Date.now() - savedTime > options.persist.expires) {
                uni.removeStorageSync(`${store.$id}-data`)
                uni.removeStorageSync(`${store.$id}-saved-time`)
            }
        }
        
        // 监听存储变化(多端同步)
        #ifdef H5
        window.addEventListener('storage', (e) => {
            if (e.key === `${store.$id}-data`) {
                store.$patch(JSON.parse(e.newValue || '{}'))
            }
        })
        #endif
    }
}

// Store配置示例
export const useAuthStore = defineStore('auth', {
    persist: {
        key: 'auth-store',
        paths: ['token', 'userInfo'],
        expires: 7 * 24 * 60 * 60 * 1000, // 7天过期
        storage: 'local' // 指定存储类型
    }
})

4.2 状态更新优化

// 使用批量更新减少渲染次数
export const useListStore = defineStore('list', {
    actions: {
        // 不推荐的写法:多次更新state
        async fetchDataBad() {
            this.loading = true
            const data = await api.getData()
            this.items = data.items  // 第一次更新
            this.total = data.total  // 第二次更新
            this.loading = false     // 第三次更新
        },
        
        // 推荐的写法:批量更新
        async fetchDataGood() {
            this.loading = true
            try {
                const data = await api.getData()
                // 使用$patch一次性更新
                this.$patch({
                    items: data.items,
                    total: data.total,
                    loading: false
                })
            } catch (error) {
                this.$patch({
                    loading: false,
                    error: error.message
                })
            }
        },
        
        // 使用action的$patch方法
        updateMultipleItems(updates: Record) {
            this.$patch((state) => {
                Object.keys(updates).forEach(key => {
                    if (key in state) {
                        state[key] = updates[key]
                    }
                })
            })
        }
    }
})

第五部分:实战案例 – 电商应用状态管理

5.1 完整的电商Store架构

// src/stores/modules/ecommerce/index.ts
import { useUserStore } from '../user'
import { useCartStore } from '../cart'
import { useProductStore } from '../product'
import { useOrderStore } from '../order'
import { useAddressStore } from '../address'

// 电商业务聚合Store
export const useEcommerceStore = defineStore('ecommerce', () => {
    const userStore = useUserStore()
    const cartStore = useCartStore()
    const productStore = useProductStore()
    const orderStore = useOrderStore()
    const addressStore = useAddressStore()
    
    // 全局加载状态
    const globalLoading = ref(false)
    
    // 初始化电商数据
    const initializeEcommerce = async () => {
        if (!userStore.isLoggedIn) return
        
        globalLoading.value = true
        try {
            // 并行加载必要数据
            await Promise.all([
                cartStore.fetchCart(),
                addressStore.fetchAddresses(),
                productStore.fetchRecommendations()
            ])
        } finally {
            globalLoading.value = false
        }
    }
    
    // 一键下单
    const quickOrder = async (productId: string, skuId: string) => {
        if (!userStore.isLoggedIn) {
            uni.navigateTo({ url: '/pages/login/index' })
            return
        }
        
        // 1. 获取商品详情
        const product = await productStore.fetchProductDetail(productId)
        
        // 2. 添加到购物车
        await cartStore.addItem({
            productId,
            skuId,
            quantity: 1,
            price: product.price
        })
        
        // 3. 创建订单
        const orderId = await orderStore.createFromCart([skuId])
        
        // 4. 跳转到支付
        uni.navigateTo({
            url: `/pages/order/payment?id=${orderId}`
        })
    }
    
    // 监听用户登录状态变化
    watch(() => userStore.isLoggedIn, (loggedIn) => {
        if (loggedIn) {
            initializeEcommerce()
        } else {
            // 用户退出,清理数据
            cartStore.clear()
            orderStore.clear()
        }
    })
    
    return {
        globalLoading,
        initializeEcommerce,
        quickOrder,
        // 暴露子store的state(按需)
        user: userStore,
        cart: cartStore,
        product: productStore,
        order: orderStore,
        address: addressStore
    }
})

5.2 跨页面状态同步方案

// src/stores/plugins/sync.ts - 跨页面状态同步插件
export function createSyncPlugin() {
    return ({ store }: PiniaPluginContext) => {
        // 监听store变化
        store.$subscribe((mutation, state) => {
            // 广播状态变化到其他页面
            #ifdef MP-WEIXIN
            const pages = getCurrentPages()
            pages.forEach(page => {
                if (page?.$vm?.$pinia) {
                    // 更新其他页面的store状态
                    page.$vm.$pinia.state.value[store.$id] = state
                }
            })
            #endif
            
            #ifdef H5
            // 使用Broadcast Channel API
            if (typeof BroadcastChannel !== 'undefined') {
                const channel = new BroadcastChannel(`pinia-${store.$id}`)
                channel.postMessage({
                    type: 'state-update',
                    state: JSON.parse(JSON.stringify(state))
                })
                channel.close()
            }
            #endif
        })
        
        // 接收其他页面的状态更新
        #ifdef H5
        if (typeof BroadcastChannel !== 'undefined') {
            const channel = new BroadcastChannel(`pinia-${store.$id}`)
            channel.onmessage = (event) => {
                if (event.data.type === 'state-update') {
                    store.$patch(event.data.state)
                }
            }
        }
        #endif
    }
}

5.3 错误处理与监控

// src/stores/plugins/errorHandler.ts
export function createErrorHandlerPlugin() {
    return ({ store }: PiniaPluginContext) => {
        const originalActions = { ...store }
        
        // 包装所有actions
        Object.keys(store).forEach(key => {
            if (typeof store[key] === 'function') {
                const originalAction = store[key]
                store[key] = async function(...args: any[]) {
                    try {
                        return await originalAction.apply(this, args)
                    } catch (error) {
                        // 统一错误处理
                        console.error(`Store ${store.$id} action ${key} error:`, error)
                        
                        // 显示错误提示
                        uni.showToast({
                            title: '操作失败,请重试',
                            icon: 'error'
                        })
                        
                        // 上报错误
                        if (process.env.NODE_ENV === 'production') {
                            // 错误上报逻辑
                            reportError(error, {
                                store: store.$id,
                                action: key,
                                args
                            })
                        }
                        
                        throw error
                    }
                }
            }
        })
    }
}

// 在main.ts中注册插件
pinia.use(createErrorHandlerPlugin())

总结:构建可维护的UniApp状态管理架构

通过本文的深入探讨,我们构建了一个完整的UniApp + Vue3 + Pinia状态管理解决方案。这套架构具有以下优势:

  • 类型安全:完整的TypeScript支持,提供更好的开发体验
  • 模块化:清晰的Store组织,便于团队协作
  • 高性能:优化的更新策略,减少不必要的渲染
  • 跨端兼容:完善的平台适配,一次编写多端运行
  • 可维护性:插件化架构,便于扩展和维护

在实际项目中,建议根据业务复杂度选择合适的架构模式。对于中小型应用,可以直接使用基础的Pinia Store;对于大型企业级应用,推荐采用本文介绍的模块化+组合式架构。

记住,良好的状态管理不仅是技术选择,更是工程实践的体现。合理规划Store结构、优化状态更新、完善错误处理,这些都能显著提升应用的质量和开发效率。

UniApp跨端状态管理深度解析:Pinia+Vue3实战企业级应用架构 | 移动开发教程
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 uniapp UniApp跨端状态管理深度解析:Pinia+Vue3实战企业级应用架构 | 移动开发教程 https://www.taomawang.com/web/uniapp/1728.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务