uView Pro

23:52项目使用 Pinia 作为状态管理库,并集成了 pinia-plugin-persistedstate 插件,实现状态的自动持久化。Pinia 是 Vue 3 官方推荐的状态管理库,提供了比 Vuex 更简洁的 API。
在 src/stores/index.ts 中:
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate'
const pinia = createPinia()
// 配置数据持久化
pinia.use(
createPersistedState({
storage: {
getItem: uni.getStorageSync, // 获取本地存储
setItem: uni.setStorageSync, // 设置本地存储
},
}),
)
export default pinia创建一个计数器 Store(src/stores/counter.ts):
import { defineStore } from 'pinia'
import { ref } from 'vue'
// defineStore 的第一个参数是 store 的唯一 ID
export const useCounterStore = defineStore('counter', () => {
// 定义状态
const count = ref(0)
// 定义 action(修改状态的方法)
function increment() {
count.value++
}
function decrement() {
if (count.value > 0) {
count.value--
}
}
function reset() {
count.value = 0
}
function setCount(n: number) {
count.value = n
}
// 返回需要暴露的状态和方法
return { count, increment, decrement, reset, setCount }
})创建一个用户 Store(src/stores/user.ts):
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
// 定义状态
const userInfo = ref({
id: '',
name: '',
avatar: '',
email: '',
})
const isLogin = ref(false)
const token = ref('')
// 计算属性
const displayName = computed(() => {
return userInfo.value.name || '游客'
})
// 登录 action
function login(credentials: any) {
return new Promise((resolve, reject) => {
// 调用登录 API
// const res = await request.post('/api/login', credentials)
// 设置状态
isLogin.value = true
token.value = 'mock_token_123'
userInfo.value = {
id: '1',
name: '张三',
avatar: 'https://example.com/avatar.jpg',
email: 'zhangsan@example.com',
}
resolve({ success: true })
})
}
// 登出 action
function logout() {
isLogin.value = false
token.value = ''
userInfo.value = {
id: '',
name: '',
avatar: '',
email: '',
}
}
// 更新用户信息
function updateUserInfo(info: any) {
userInfo.value = { ...userInfo.value, ...info }
}
// 设置 token
function setToken(newToken: string) {
token.value = newToken
}
return {
userInfo,
isLogin,
token,
displayName,
login,
logout,
updateUserInfo,
setToken,
}
}, {
persist: true // 启用持久化
})<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
// 访问状态
const count = computed(() => counterStore.count)
// 调用 action
const handleIncrement = () => {
counterStore.increment()
}
</script>
<template>
<view class="counter">
<view class="count">{{ count }}</view>
<u-button @click="handleIncrement">增加</u-button>
<u-button @click="counterStore.decrement">减少</u-button>
<u-button @click="counterStore.reset">重置</u-button>
</view>
</template><script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
</script>
<template>
<!-- 无需 computed,直接使用 -->
<view class="count">{{ counterStore.count }}</view>
</template>import { storeToRefs } from 'pinia'
const counterStore = useCounterStore()
// 使用 storeToRefs 保持响应性
const { count } = storeToRefs(counterStore)
const { increment, decrement } = counterStore创建 src/hooks/useUserAuth.ts:
import { computed } from 'vue'
import { useUserStore } from '@/stores/user'
export function useUserAuth() {
const userStore = useUserStore()
const isLogin = computed(() => userStore.isLogin)
const userName = computed(() => userStore.displayName)
const login = async (email: string, password: string) => {
try {
await userStore.login({ email, password })
return true
} catch (error) {
console.error('登录失败:', error)
return false
}
}
const logout = () => {
userStore.logout()
}
return {
isLogin,
userName,
login,
logout,
}
}在组件中使用:
<script setup lang="ts">
import { useUserAuth } from '@/composables/useUserAuth'
const { isLogin, userName, login, logout } = useUserAuth()
const handleLogin = async () => {
const success = await login('user@example.com', 'password')
if (success) {
console.log('登录成功')
}
}
</script>
<template>
<view v-if="isLogin" class="user-panel">
<text>欢迎 {{ userName }}</text>
<u-button @click="logout">登出</u-button>
</view>
<view v-else class="login-panel">
<u-button @click="handleLogin">登录</u-button>
</view>
</template>// stores/notifications.ts
export const useNotificationsStore = defineStore('notifications', () => {
const notifications = ref([])
function addNotification(message: string, type: string = 'info') {
notifications.value.push({
id: Date.now(),
message,
type,
})
}
return { notifications, addNotification }
})
// stores/user.ts
export const useUserStore = defineStore('user', () => {
const notificationsStore = useNotificationsStore()
async function login(credentials: any) {
try {
// 登录逻辑
notificationsStore.addNotification('登录成功', 'success')
} catch (error) {
notificationsStore.addNotification('登录失败', 'error')
}
}
return { login }
})import { watch } from 'vue'
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
// 监听 count 的变化
watch(
() => counterStore.count,
(newCount) => {
console.log('count 更新为:', newCount)
}
)
// 订阅整个 store 的变化
counterStore.$subscribe((mutation, state) => {
console.log('Store 变化:', mutation, state)
})虽然 Setup Store 中可以使用 computed,但也可以定义 getter:
export const useStoreWithGetter = defineStore('example', () => {
const list = ref([1, 2, 3, 4, 5])
const evenNumbers = computed(() => {
return list.value.filter(n => n % 2 === 0)
})
return { list, evenNumbers }
})export const usePostStore = defineStore('post', () => {
const posts = ref([])
const loading = ref(false)
async function fetchPosts() {
loading.value = true
try {
// const { request } = useHttp()
// const data = await request.get('/api/posts')
// posts.value = data
posts.value = [
{ id: 1, title: '文章 1' },
{ id: 2, title: '文章 2' },
]
} finally {
loading.value = false
}
}
async function createPost(title: string) {
// const { request } = useHttp()
// const newPost = await request.post('/api/posts', { title })
const newPost = { id: Date.now(), title }
posts.value.push(newPost)
return newPost
}
return {
posts,
loading,
fetchPosts,
createPost,
}
})默认情况下,所有状态都会被持久化。如果只想持久化部分字段:
export const useUserStore = defineStore('user', () => {
const token = ref('')
const tempData = ref('') // 不持久化
return { token, tempData }
}, {
persist: {
paths: ['token'] // 只持久化 token
}
})export const useCustomStore = defineStore('custom', () => {
// ...
}, {
persist: {
storage: localStorage, // 使用 localStorage 而不是 sessionStorage
key: 'my_custom_store' // 自定义存储 key
}
})安装 Vue DevTools 扩展,可以:
const counterStore = useCounterStore()
// 查看完整的 store 对象
console.log(counterStore.$state)
// 查看所有 action
console.log(counterStore.$getters)
// 查看订阅信息
console.log(counterStore.$id)use[PascalCase]Store 命名规范确保在 Setup Store 中使用 ref() 定义状态,而不是普通对象。
检查:
使用 Store 的 $subscribe 方法监听变化,或在 action 中调用其他 Store 的 action。
