Vue3 狀態(tài)管理 Pinia 入門指南
很多剛接觸 Vue3 狀態(tài)管理的同學(xué)都會(huì)問(wèn):“Vuex 還沒(méi)學(xué)明白,怎么又出了個(gè) Pinia?”這篇就來(lái)聊聊 Pinia,看看它到底是什么、為什么要用它、以及怎么用好它。
- Pinia 是什么:Pinia 是 Vue 的全新?tīng)顟B(tài)管理庫(kù),它和 Vuex 有哪些核心區(qū)別?
- 如何定義 Store:掌握選項(xiàng)式和組合式兩種創(chuàng)建 Store 的風(fēng)格。
- 在組件中如何使用:學(xué)會(huì)在
setup中正確地使用 Store 并保持響應(yīng)性。
一、使用場(chǎng)景
Pinia 適用于需要對(duì) Vue 應(yīng)用進(jìn)行全局狀態(tài)管理的場(chǎng)景:
- 跨組件共享狀態(tài):例如用戶登錄信息、購(gòu)物車數(shù)據(jù)、主題配置等
- 與后端 API 交互:集中管理 API 調(diào)用和數(shù)據(jù)緩存邏輯
- 調(diào)試與開(kāi)發(fā)體驗(yàn):配合 Vue DevTools 進(jìn)行狀態(tài)追蹤和時(shí)間旅行
- 類型安全:為 TypeScript 項(xiàng)目提供完整的類型推導(dǎo)支持
- 模塊化管理:將不同領(lǐng)域的全局狀態(tài)拆分為獨(dú)立的 Store,易于維護(hù)和擴(kuò)展
二、注意事項(xiàng)
劃重點(diǎn): Pinia 在 Vue 2 和 Vue 3 中均可使用,但本文所有示例均基于 Vue 3 + Script Setup 語(yǔ)法。
- ??
defineStore?? 的 ID 必須唯一:每個(gè) Store 都需要一個(gè)唯一的 ID,用于 DevTools 的連接 - 沒(méi)有 ?
mutations??:與 Vuex 不同,Pinia 將狀態(tài)(state?)與變更邏輯(actions)直接統(tǒng)一,摒棄了繁瑣的 mutations - 避免過(guò)度使用全局狀態(tài):組件內(nèi)部能處理的數(shù)據(jù),不要提升到 Store 中
三、基本用法
3.1 安裝與配置
yarn add pinia # 或 npm install pinia
在 main.ts 中創(chuàng)建 Pinia 實(shí)例并注冊(cè)到 Vue 應(yīng)用:
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
3.2 定義 Store
Pinia 提供兩種風(fēng)格來(lái)定義 Store:選項(xiàng)式(Options API) 和 組合式(Composition API) 。選擇哪個(gè)全憑個(gè)人喜好,但組合式風(fēng)格更適合復(fù)雜邏輯的復(fù)用。
選項(xiàng)式風(fēng)格:
// stores/counter.ts
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
// 狀態(tài)(數(shù)據(jù))
state: () => ({
count: 0,
name: '我的計(jì)數(shù)器',
}),
// 計(jì)算屬性(派生數(shù)據(jù))
getters: {
// 使用箭頭函數(shù)時(shí),state 為第一個(gè)參數(shù)
doubleCount: (state) => state.count * 2,
// 需要訪問(wèn)其他 getter 時(shí),使用普通函數(shù)
doubleCountPlusOne(): number {
return this.doubleCount + 1
},
},
// 操作方法(業(yè)務(wù)邏輯)
actions: {
increment() {
this.count++
},
decrement() {
this.count--
},
// 支持異步操作
async incrementAsync() {
await new Promise((resolve) => setTimeout(resolve, 1000))
this.increment()
},
incrementBy(amount: number) {
this.count += amount
},
},
})
組合式風(fēng)格(推薦):
// stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { api } from '@/services/api' // 假設(shè)的 API 服務(wù)
// 類型定義
interface User {
id: number
name: string
email: string
}
interface LoginCredentials {
username: string
password: string
}
export const useUserStore = defineStore('user', () => {
// 狀態(tài)(類似 options 的 data)
const user = ref<User | null>(null)
const isLoggedIn = ref(false)
// 計(jì)算屬性(類似 options 的 getters)
const userName = computed(() => user.value?.name || '游客')
const userId = computed(() => user.value?.id || 0)
// 操作方法(類似 options 的 actions)
function login(credentials: LoginCredentials) {
return api.login(credentials).then((response) => {
user.value = response.user
isLoggedIn.value = true
return response
})
}
function logout() {
user.value = null
isLoggedIn.value = false
}
async function fetchUserProfile() {
if (!userId.value) return
try {
const profile = await api.getUserProfile(userId.value)
user.value = { ...user.value, ...profile }
} catch (error) {
console.error('獲取用戶資料失敗:', error)
}
}
// 返回需要暴露給外部的所有內(nèi)容
return { user, isLoggedIn, userName, userId, login, logout, fetchUserProfile }
})
代碼解析:
- ??defineStore?:這是定義 Store 的核心函數(shù),第一個(gè)參數(shù)是 Store 的唯一 ID,第二個(gè)參數(shù)是 Store 的定義(可以是對(duì)象或函數(shù))。
- ??ref?? 和 ?computed??:在組合式風(fēng)格中,我們使用 Vue 的響應(yīng)式 API 來(lái)定義狀態(tài)和計(jì)算屬性,和寫(xiě)組件 script setup 一模一樣。
- ??return?? 語(yǔ)句:組合式 Store 必須 return 一個(gè)對(duì)象,對(duì)象中的屬性和方法才能被外部訪問(wèn)。
3.3 在組件中使用 Store
<template>
<div>
<h1>{{ counterStore.name }}</h1>
<p>計(jì)數(shù): {{ counterStore.count }}</p>
<p>雙倍計(jì)數(shù): {{ counterStore.doubleCount }}</p>
<p>用戶名: {{ userStore.userName }}</p>
<button @click="counterStore.increment()">增加</button>
<button @click="counterStore.decrement()">減少</button>
<button @click="counterStore.incrementBy(5)">增加5</button>
<button @click="counterStore.incrementAsync()">異步增加</button>
<button @click="login" v-if="!userStore.isLoggedIn">登錄</button>
<button @click="userStore.logout()" v-else>退出</button>
</div>
</template>
<script lang="ts" setup>
import { watch } from 'vue'
import { useCounterStore } from '@/stores/counter'
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'
const counterStore = useCounterStore()
const userStore = useUserStore()
// 關(guān)鍵:使用 storeToRefs 保持解構(gòu)后的響應(yīng)性
const { count, doubleCount } = storeToRefs(counterStore)
const { userName, isLoggedIn } = storeToRefs(userStore)
// 直接調(diào)用 action(action 本身就是方法,無(wú)需解構(gòu))
const login = () => {
userStore
.login({ username: 'admin', password: 'password' })
.then(() => {
console.log('登錄成功')
})
.catch((error) => {
console.error('登錄失敗:', error)
})
}
// 監(jiān)聽(tīng) Store 狀態(tài)變化
watch(
() => counterStore.count,
(newCount, oldCount) => {
console.log(`計(jì)數(shù)從 ${oldCount} 變?yōu)?${newCount}`)
}
)
</script>
代碼解析:
- ??storeToRefs??:這是 Pinia 提供的一個(gè)工具函數(shù),用于“解包” Store 的 state 和 getters。直接從 Store 解構(gòu) const { count } = counterStore? 會(huì)失去響應(yīng)性,而 storeToRefs? 保證了解構(gòu)后的每個(gè) ref 依然是響應(yīng)式的。
- 直接調(diào)用 ?actions??:actions? 是普通函數(shù),可以直接從 Store 對(duì)象上調(diào)用,不需要解構(gòu)。counterStore.increment()? 和 userStore.login() 都是正確的用法。
- ??watch?? 監(jiān)聽(tīng):你可以像監(jiān)聽(tīng)普通 ref? 一樣監(jiān)聽(tīng) Store 中的狀態(tài),使用 watch(() => counterStore.count, handler)。
剛接觸 Pinia,建議先從最典型的狀態(tài)(比如用戶登錄信息、全局配置)開(kāi)始使用,不要一開(kāi)始就把所有組件數(shù)據(jù)都放進(jìn) Store。寫(xiě)得順手了,再逐步將那些頻繁跨組件傳遞的 props 和 emit 改為 Store 管理,你會(huì)發(fā)現(xiàn)代碼瞬間清爽許多。
到此這篇關(guān)于Vue3 狀態(tài)管理 Pinia 入門指南的文章就介紹到這了,更多相關(guān)Vue3 狀態(tài)管理 Pinia內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Vue3之狀態(tài)管理器(Pinia)詳解及使用方式
- Vue3中的pinia使用方法總結(jié)(建議收藏版)
- Vue3狀態(tài)管理之Pinia的入門使用教程
- vue3項(xiàng)目使用pinia狀態(tài)管理器的使用
- vue3項(xiàng)目引入pinia報(bào)錯(cuò)的簡(jiǎn)單解決
- vue3 pinia踩坑及解決方案詳解
- Vue3中pinia的使用與持久化處理詳解
- Vue3組件不發(fā)生變化如何監(jiān)聽(tīng)pinia中數(shù)據(jù)變化
- vue3中vuex與pinia的踩坑筆記記錄
- vue3搭配pinia的踩坑實(shí)戰(zhàn)記錄
- vue3?pinia實(shí)現(xiàn)持久化詳解
- vue3中使用pinia(大菠蘿)狀態(tài)管理倉(cāng)庫(kù)的項(xiàng)目實(shí)踐
相關(guān)文章
vue解決子組件樣式覆蓋問(wèn)題scoped deep
文章主要介紹了在Vue項(xiàng)目中處理全局樣式和局部樣式的方法,包括使用scoped屬性和深度選擇器(/deep/)來(lái)覆蓋子組件的樣式,作者建議所有組件必須使用scoped,以避免樣式?jīng)_突,對(duì)于父組件覆蓋子組件的樣式,作者推薦給子組件指定自定義類名2025-01-01
Vue Form表單的使用方法(rules格式校驗(yàn)網(wǎng)絡(luò)校驗(yàn)鍵盤(pán)按鍵監(jiān)聽(tīng))
本文介紹了在Vue.js中使用表單校驗(yàn)規(guī)則(rules)進(jìn)行網(wǎng)絡(luò)請(qǐng)求校驗(yàn)的方法,以及如何通過(guò)formRef引用表單對(duì)象并進(jìn)行鍵盤(pán)按鍵監(jiān)聽(tīng),感興趣的朋友跟隨小編一起看看吧2024-11-11
vue移動(dòng)端使用appClound拉起支付寶支付的實(shí)現(xiàn)方法
這篇文章主要介紹了vue移動(dòng)端使用appClound拉起支付寶支付的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
vue導(dǎo)出PDF實(shí)現(xiàn)方式(vue2,插件:pdfmake,表格)
文章主要介紹了如何使用pdfmake插件生成PDF文件,并提供了一個(gè)示例代碼,用于將嵌套的數(shù)組轉(zhuǎn)換為一維數(shù)組,以便在PDF中生成表格,同時(shí),文章還討論了如何設(shè)置表格的樣式,包括顏色、背景色和文字位置等2026-03-03
去除element-ui下拉框的下拉箭頭的實(shí)現(xiàn)
我們最開(kāi)始拿到的element-ui是帶有下拉箭頭的,那么如何去除element-ui下拉框的下拉箭頭的實(shí)現(xiàn),本文就詳細(xì)的介紹一下,感興趣的可以了解一下2023-08-08
vue+swiper實(shí)現(xiàn)組件化開(kāi)發(fā)的實(shí)例代碼
這篇文章主要介紹了vue+swiper實(shí)現(xiàn)組件化開(kāi)發(fā)的相關(guān)資料,需要的朋友可以參考下2017-10-10
詳解在vue-cli項(xiàng)目中使用mockjs(請(qǐng)求數(shù)據(jù)刪除數(shù)據(jù))
本篇文章主要介紹了在vue-cli項(xiàng)目中使用mockjs(請(qǐng)求數(shù)據(jù)刪除數(shù)據(jù)),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
Vue 數(shù)據(jù)綁定從 v-bind 到 v-model 的實(shí)戰(zhàn)案例
本文將系統(tǒng)解析Vue中兩種核心數(shù)據(jù)綁定方式,單向綁定 (v-bind) 和雙向綁定 (v-model),通過(guò)實(shí)例講解其工作原理、使用場(chǎng)景及最佳實(shí)踐,幫助你真正掌握 Vue 數(shù)據(jù)驅(qū)動(dòng)的精髓,感興趣的朋友跟隨小編一起看看吧2025-10-10

