云计算百科
云计算领域专业知识百科平台

(Vue3入门教程)Vue3状态管理Pinia用法详解

Pinia 简介

Pinia是Vue3的官方状态管理库,它提供了简单、直观的状态管理解决方案,与Vue3的Composition API完美集成。

主要特性

  • TypeScript支持:完整的TypeScript类型推断
  • 轻量级:体积小,性能优秀
  • DevTools支持:优秀的开发工具支持
  • 模块化:支持多个Store
  • 组合式API:与Vue3 Composition API完美集成

Store 的定义和创建

基础Store定义

// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
// 状态
const count = ref(0)
const name = ref('Counter Store')

// 计算属性
const doubleCount = computed(() => count.value * 2)
const isEven = computed(() => count.value % 2 === 0)

// 方法
function increment() {
count.value++
}

function decrement() {
count.value–
}

function reset() {
count.value = 0
}

function setCount(value) {
count.value = value
}

// 异步方法
async function incrementAsync() {
await new Promise(resolve => setTimeout(resolve, 1000))
count.value++
}

return {
// 状态
count,
name,
// 计算属性
doubleCount,
isEven,
// 方法
increment,
decrement,
reset,
setCount,
incrementAsync
}
})

Store 的使用

在组件中使用Store

<!– components/CounterDemo.vue –>
<template>
<div class="counter-demo">
<h2>计数器示例</h2>

<!– 直接使用Store状态 –>
<div class="counter-info">
<p>当前计数:{{ counterStore.count }}</p>
<p>双倍计数:{{ counterStore.doubleCount }}</p>
<p>是否偶数:{{ counterStore.isEven ? '是' : '否' }}</p>
<p>Store名称:{{ counterStore.name }}</p>
</div>

<!– 操作按钮 –>
<div class="counter-actions">
<button @click="counterStore.increment">+1</button>
<button @click="counterStore.decrement">-1</button>
<button @click="counterStore.reset">重置</button>
<button @click="counterStore.incrementAsync" :disabled="loading">
{{ loading ? '加载中…' : '异步+1' }}
</button>
</div>

<!– 自定义输入 –>
<div class="counter-input">
<input v-model.number="newValue" type="number" placeholder="设置新值">
<button @click="setCustomValue">设置</button>
</div>

<!– 用户Store示例 –>
<div class="user-info" v-if="userStore.isAuthenticated">
<h3>用户信息</h3>
<p>用户名:{{ userStore.userName }}</p>
<p>用户信息:{{ userStore.userInfo }}</p>
<p>是否有管理权限:{{ userStore.hasPermission('admin') ? '是' : '否' }}</p>
<button @click="userStore.logout">登出</button>
</div>

<div v-else class="login-form">
<h3>登录</h3>
<input v-model="loginForm.username" placeholder="用户名">
<input v-model="loginForm.password" type="password" placeholder="密码">
<button @click="handleLogin" :disabled="loginLoading">
{{ loginLoading ? '登录中…' : '登录' }}
</button>
</div>
</div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useCounterStore } from '../stores/counter'
import { useUserStore } from '../stores/user'

// 使用Store
const counterStore = useCounterStore()
const userStore = useUserStore()

// 本地状态
const newValue = ref(0)
const loading = ref(false)
const loginForm = ref({
username: '',
password: ''
})
const loginLoading = ref(false)

// 设置自定义值
const setCustomValue = () => {
counterStore.setCount(newValue.value)
}

// 处理登录
const handleLogin = async () => {
if (!loginForm.value.username || !loginForm.value.password) {
alert('请输入用户名和密码')
return
}

loginLoading.value = true
try {
const result = await userStore.login(loginForm.value)
if (result.success) {
alert('登录成功!')
loginForm.value = { username: '', password: '' }
} else {
alert(`登录失败:${result.error}`)
}
} finally {
loginLoading.value = false
}
}

// 组件挂载时初始化用户状态
onMounted(async () => {
await userStore.initUser()
})
</script>

<style scoped>
.counter-demo {
padding: 20px;
max-width: 600px;
margin: 0 auto;
}

.counter-info {
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}

.counter-actions {
display: flex;
gap: 10px;
margin-bottom: 20px;
}

.counter-actions button {
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}

.counter-actions button:hover {
background: #0056b3;
}

.counter-actions button:disabled {
background: #6c757d;
cursor: not-allowed;
}

.counter-input {
display: flex;
gap: 10px;
margin-bottom: 20px;
}

.counter-input input {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
flex: 1;
}

.counter-input button {
padding: 8px 16px;
background: #28a745;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}

.user-info,
.login-form {
background: #e9ecef;
padding: 15px;
border-radius: 8px;
margin-top: 20px;
}

.login-form input {
display: block;
width: 100%;
padding: 8px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 4px;
}

.login-form button {
width: 100%;
padding: 10px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>

高级状态管理

购物车Store示例

// stores/cart.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCartStore = defineStore('cart', () => {
// 状态
const items = ref([])
const isLoading = ref(false)

// 计算属性
const totalItems = computed(() => {
return items.value.reduce((total, item) => total + item.quantity, 0)
})

const totalPrice = computed(() => {
return items.value.reduce((total, item) => total + item.price * item.quantity, 0)
})

const isEmpty = computed(() => items.value.length === 0)

const itemCount = computed(() => items.value.length)

// 方法
function addItem(product, quantity = 1) {
const existingItem = items.value.find(item => item.id === product.id)

if (existingItem) {
existingItem.quantity += quantity
} else {
items.value.push({
…product,
quantity
})
}
}

function removeItem(productId) {
const index = items.value.findIndex(item => item.id === productId)
if (index > -1) {
items.value.splice(index, 1)
}
}

function updateQuantity(productId, quantity) {
const item = items.value.find(item => item.id === productId)
if (item) {
if (quantity <= 0) {
removeItem(productId)
} else {
item.quantity = quantity
}
}
}

function clearCart() {
items.value = []
}

// 异步方法
async function checkout() {
if (isEmpty.value) {
throw new Error('购物车为空')
}

isLoading.value = true
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 2000))

// 清空购物车
clearCart()

return { success: true, message: '订单提交成功!' }
} catch (error) {
throw new Error('订单提交失败:' + error.message)
} finally {
isLoading.value = false
}
}

// 持久化
function saveToLocalStorage() {
localStorage.setItem('cart', JSON.stringify(items.value))
}

function loadFromLocalStorage() {
const saved = localStorage.getItem('cart')
if (saved) {
items.value = JSON.parse(saved)
}
}

return {
// 状态
items,
isLoading,
// 计算属性
totalItems,
totalPrice,
isEmpty,
itemCount,
// 方法
addItem,
removeItem,
updateQuantity,
clearCart,
checkout,
saveToLocalStorage,
loadFromLocalStorage
}
})

产品Store示例

// stores/products.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useProductsStore = defineStore('products', () => {
// 状态
const products = ref([])
const categories = ref([])
const loading = ref(false)
const error = ref(null)
const filters = ref({
category: '',
search: '',
sortBy: 'name',
sortOrder: 'asc'
})

// 计算属性
const filteredProducts = computed(() => {
let filtered = products.value

// 按分类筛选
if (filters.value.category) {
filtered = filtered.filter(product =>
product.category === filters.value.category
)
}

// 按搜索词筛选
if (filters.value.search) {
const searchLower = filters.value.search.toLowerCase()
filtered = filtered.filter(product =>
product.name.toLowerCase().includes(searchLower) ||
product.description.toLowerCase().includes(searchLower)
)
}

// 排序
filtered.sort((a, b) => {
const aValue = a[filters.value.sortBy]
const bValue = b[filters.value.sortBy]

if (filters.value.sortOrder === 'asc') {
return aValue > bValue ? 1 : -1
} else {
return aValue < bValue ? 1 : -1
}
})

return filtered
})

const availableCategories = computed(() => {
return […new Set(products.value.map(p => p.category))]
})

// 方法
async function fetchProducts() {
loading.value = true
error.value = null

try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1000))

// 模拟产品数据
products.value = [
{ id: 1, name: 'Vue3教程', price: 99, category: '教育', description: 'Vue3完整学习教程' },
{ id: 2, name: 'React教程', price: 88, category: '教育', description: 'React开发指南' },
{ id: 3, name: 'TypeScript教程', price: 77, category: '教育', description: 'TypeScript入门' },
{ id: 4, name: 'Node.js教程', price: 66, category: '教育', description: 'Node.js后端开发' },
{ id: 5, name: 'Python教程', price: 55, category: '教育', description: 'Python编程基础' },
{ id: 6, name: 'Java教程', price: 44, category: '教育', description: 'Java企业级开发' }
]
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}

function setFilter(key, value) {
filters.value[key] = value
}

function clearFilters() {
filters.value = {
category: '',
search: '',
sortBy: 'name',
sortOrder: 'asc'
}
}

function getProductById(id) {
return products.value.find(product => product.id === id)
}

return {
// 状态
products,
categories,
loading,
error,
filters,
// 计算属性
filteredProducts,
availableCategories,
// 方法
fetchProducts,
setFilter,
clearFilters,
getProductById
}
})

小结

学习要点回顾

  • Pinia基础:Store的定义和创建,组合式API风格
  • 状态管理:状态、计算属性、方法的组织和使用
  • Store组合:多个Store的协作和组合
  • 实际应用:购物车、用户管理等实际场景
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » (Vue3入门教程)Vue3状态管理Pinia用法详解
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!