大家好,我是 V 哥。Pinia 是 Vue 的狀態(tài)管理庫,它提供了一種更簡單、更不規(guī)范的 API 來管理應(yīng)用的狀態(tài)。Pinia 的設(shè)計哲學(xué)是簡單性和易用性,它避免了 Vuex 中的許多復(fù)雜概念,如 mutations 和模塊的嵌套結(jié)構(gòu),提供了一種更現(xiàn)代、更符合 Vue 3 Composition API 風(fēng)格的狀態(tài)管理方式。
先來瞅一眼 Pinia 的核心組件主要包括以下幾個方面:
下面是每個部分的詳細介紹。
在 Pinia 中,Store
是用來封裝應(yīng)用的狀態(tài)和邏輯的核心概念。它允許你將狀態(tài)和行為集中管理,而不是分散在各個組件中。Store
可以包含以下幾部分:
下面是一個創(chuàng)建 Store
的步驟解析,包括代碼示例:
首先,你需要從 Pinia 導(dǎo)入 defineStore
函數(shù),并使用它來定義一個新的 Store
。
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
users: [] // 初始狀態(tài)是一個空數(shù)組
}),
getters: {
// 一個 getter 函數(shù),返回數(shù)組中的用戶數(shù)量
count: (state) => state.users.length
},
actions: {
// 一個 action 函數(shù),用于添加用戶
addUser(user) {
this.users.push(user)
}
}
})
在 Vue 組件中,你可以通過調(diào)用你定義的 useUserStore
函數(shù)來使用這個 Store
。
<template>
<div>
<button @click="addNewUser">添加用戶</button>
<p>用戶總數(shù): {{ userCount }}</p>
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
</div>
</template>
<script setup>
import { computed } from 'vue'
import useUserStore from '@/stores/user'
const store = useUserStore()
const users = computed(() => store.users)
const userCount = computed(() => store.count)
function addNewUser() {
store.addUser({ id: Date.now(), name: '新用戶' })
}
</script>
defineStore
:從 Pinia 導(dǎo)入 defineStore
函數(shù)來定義新的 Store
。useUserStore
:創(chuàng)建一個名為 useUserStore
的函數(shù),它返回一個 Store
對象。Store
中定義了一個狀態(tài) users
,初始為空數(shù)組。count
getter,它返回 users
數(shù)組的長度。addUser
action,它接受一個用戶對象并將其添加到 users
數(shù)組中。<script setup>
塊中,通過調(diào)用 useUserStore
來獲取 Store
實例,并使用 computed
來創(chuàng)建響應(yīng)式的 users
和 userCount
。addNewUser
函數(shù),當(dāng)按鈕被點擊時,調(diào)用 store.addUser
來添加新用戶。
以上案例展示了如何在 Pinia 中創(chuàng)建和管理狀態(tài),以及如何在 Vue 組件中使用 Store
。通過這種方式,你可以集中管理狀態(tài),使得狀態(tài)邏輯更加清晰和可維護。
defineStore()
是 Pinia 中用于定義 Store 的函數(shù)。它允許你以聲明式的方式創(chuàng)建一個狀態(tài)管理單元,這個單元可以包含狀態(tài)(state)、獲取器(getters)、動作(actions)等。defineStore()
函數(shù)接受一個唯一的 ID 和一個配置對象,配置對象中可以定義 state、getters、actions 等屬性。
下面是一個使用 defineStore()
創(chuàng)建 Store 的步驟解析,包括代碼示例:
首先,需要從 Pinia 導(dǎo)入 defineStore
函數(shù)。
import { defineStore } from 'pinia'
使用 defineStore()
定義一個新的 Store,傳入一個唯一的 ID 和一個配置對象。
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] // 購物車初始狀態(tài)為空數(shù)組
}),
getters: {
// 計算屬性,返回購物車中商品的總數(shù)量
itemCount(state) {
return state.items.reduce((total, item) => total + item.quantity, 0)
}
},
actions: {
// 添加商品到購物車的動作
addItem(item) {
const index = this.items.findIndex(i => i.id === item.id)
if (index > -1) {
// 如果商品已存在,則增加數(shù)量
this.items[index].quantity += item.quantity
} else {
// 否則,添加新商品
this.items.push(item)
}
},
// 清空購物車的動作
clearCart() {
this.items = []
}
}
})
在 Vue 組件中,通過調(diào)用 useCartStore
來使用這個 Store。
<template>
<div>
<button @click="addItem">添加商品</button>
<button @click="clearCart">清空購物車</button>
<p>商品總數(shù): {{ itemCount }}</p>
<ul>
<li v-for="item in cartItems" :key="item.id">
{{ item.name }} - 數(shù)量: {{ item.quantity }}
</li>
</ul>
</div>
</template>
<script setup>
import { computed } from 'vue'
import useCartStore from '@/stores/cart'
const store = useCartStore()
const cartItems = computed(() => store.items)
const itemCount = computed(() => store.itemCount)
function addItem() {
store.addItem({ id: 1, name: '商品A', quantity: 1 })
}
function clearCart() {
store.clearCart()
}
</script>
defineStore
函數(shù)。useCartStore
的函數(shù),它返回一個配置好的 Store
對象。Store
中定義了一個狀態(tài) items
,初始為空數(shù)組,用于存儲購物車中的商品。itemCount
getter,它通過遍歷 items
數(shù)組并累加每個商品的 quantity
來計算總數(shù)量。addItem
和 clearCart
兩個 actions。addItem
用于向購物車添加商品,如果商品已存在則增加其數(shù)量;clearCart
用于清空購物車。<script setup>
塊中,通過調(diào)用 useCartStore
來獲取 Store
實例,并使用 computed
來創(chuàng)建響應(yīng)式的 cartItems
和 itemCount
。addItem
和 clearCart
函數(shù),分別用于添加商品和清空購物車。
使用 defineStore()
創(chuàng)建一個包含狀態(tài)、獲取器和動作的 Store,并在 Vue 組件中使用這個 Store 來管理購物車的狀態(tài)。通過這種方式,你可以將狀態(tài)邏輯封裝在 Store 中,使得組件更加簡潔和易于管理。
reactive()
是 Vue 3 的 Composition API 中的一個函數(shù),它用于創(chuàng)建響應(yīng)式的狀態(tài)對象。當(dāng)使用 reactive()
創(chuàng)建一個對象后,Vue 會追蹤這個對象中屬性的讀取和修改,并且在數(shù)據(jù)變化時通知依賴于這些數(shù)據(jù)的組件重新渲染。
Pinia 與 Vue 的響應(yīng)式系統(tǒng)緊密集成,reactive()
通常在定義 Store 的狀態(tài)時使用。在 Pinia 中,狀態(tài)(state)是一個通過 reactive()
創(chuàng)建的響應(yīng)式對象,因此任何對狀態(tài)的修改都會自動觸發(fā)與該狀態(tài)相關(guān)的組件更新。
下面是一個使用 reactive()
來創(chuàng)建響應(yīng)式狀態(tài)的步驟解析:
首先,需要從 Vue 導(dǎo)入 reactive
函數(shù)。
import { reactive } from 'vue'
使用 reactive()
函數(shù)來創(chuàng)建一個響應(yīng)式的狀態(tài)對象。
const state = reactive({
count: 0, // 初始狀態(tài)
message: 'Hello, Pinia!' // 初始消息
})
在 Pinia 的 defineStore()
中,可以直接使用 reactive()
來定義狀態(tài)。
import { defineStore } from 'pinia'
import { reactive } from 'vue'
export const useMyStore = defineStore('myStore', {
state: () => reactive({
count: 0,
message: 'Hello, Pinia!'
}),
// 其他 getters 和 actions 可以在這里定義
})
在 Vue 組件中,通過調(diào)用 useMyStore
來使用這個 Store,并訪問響應(yīng)式狀態(tài)。
<template>
<div>
<p>Count: {{ count }}</p>
<p>Message: {{ message }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script setup>
import { computed } from 'vue'
import useMyStore from '@/stores/myStore'
const store = useMyStore()
const count = computed(() => store.state.count)
const message = computed(() => store.state.message)
function increment() {
store.state.count++
}
</script>
reactive
函數(shù)。reactive()
創(chuàng)建一個包含 count
和 message
的響應(yīng)式狀態(tài)對象。defineStore()
的 state
函數(shù)中返回一個 reactive()
對象,這樣 Pinia 就可以管理這個狀態(tài)的響應(yīng)性。<script setup>
塊中,通過調(diào)用 useMyStore
來獲取 Store
實例。使用 computed
來確保訪問狀態(tài)時保持響應(yīng)性。increment
函數(shù),當(dāng)按鈕被點擊時,直接修改 store.state.count
,這會觸發(fā)組件的更新。通過這種方式,你可以確保狀態(tài)的任何變化都會自動傳播到使用這些狀態(tài)的組件中,實現(xiàn)響應(yīng)式的數(shù)據(jù)流,你get到了嗎。
Vue Devtools 是一個瀏覽器擴展,它為開發(fā) Vue 應(yīng)用提供了強大的調(diào)試支持。對于 Pinia 來說,Devtools 支持意味著你可以在開發(fā)過程中更直觀地查看和操作應(yīng)用的狀態(tài)。
Pinia 與 Vue Devtools 集成,提供了以下功能:
要充分利用 Pinia 的 Devtools 支持,你需要確保正確安裝和配置了 Vue Devtools,并且正確地在你的 Pinia Store 中編寫代碼。來吧,一步一步跟著做就行:
首先,確保你已經(jīng)安裝了 Vue Devtools 瀏覽器擴展。你可以從 Chrome Web Store 或 Firefox Add-ons 等地方安裝。
創(chuàng)建一個 Pinia Store,并定義 state、getters 和 actions。
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
在你的 Vue 應(yīng)用中創(chuàng)建 Pinia 實例,并在應(yīng)用啟動時使用它。
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')
在組件中使用你的 Store,并執(zhí)行一些狀態(tài)更改的動作。
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script setup>
import { computed } from 'vue'
import useCounterStore from '@/stores/counter'
const store = useCounterStore()
const count = computed(() => store.count)
function increment() {
store.increment()
}
</script>
defineStore
創(chuàng)建一個包含 state、getters 和 actions 的 Pinia Store。利用 Vue Devtools 提供的功能來調(diào)試使用 Pinia 管理狀態(tài)的 Vue 應(yīng)用,感覺是不是挺爽。
Pinia 的插件系統(tǒng)允許開發(fā)者擴展 Pinia 的功能。插件可以訪問 Pinia 的 Store 創(chuàng)建過程,可以執(zhí)行以下操作:
state
、getters
、actions
等屬性。Pinia 插件通常在創(chuàng)建 Pinia 實例時注冊,然后 Pinia 會將插件應(yīng)用到每個創(chuàng)建的 Store 上。
下面是一個創(chuàng)建和使用 Pinia 插件的步驟解析,包括代碼示例:
首先,定義一個插件函數(shù),該函數(shù)接收 Pinia 的實例作為參數(shù)。
function myPiniaPlugin(pinia) {
// 插件邏輯
}
在插件函數(shù)內(nèi)部,可以訪問 Pinia 的 store
對象,并對其進行操作。
function myPiniaPlugin(pinia) {
pinia.use((store) => {
// 可以在此處訪問 store.state, store.getters, store.actions 等
// 例如,向 store 添加一個新屬性
store.myCustomProperty = 'Hello from plugin!'
})
}
創(chuàng)建 Pinia 實例時,使用 use
方法注冊插件。
import { createPinia } from 'pinia'
const pinia = createPinia().use(myPiniaPlugin)
定義一個 Store,使用 defineStore
函數(shù)。
import { defineStore } from 'pinia'
export const useMyStore = defineStore('myStore', {
// state, getters, actions 定義
state: () => ({
value: 0
}),
// 其他選項...
})
在組件中使用 Store,并訪問插件添加的屬性。
<template>
<div>
<p>Value: {{ value }}</p>
<p>Plugin Property: {{ store.myCustomProperty }}</p>
</div>
</template>
<script setup>
import { computed } from 'vue'
import useMyStore from '@/stores/myStore'
const store = useMyStore()
const value = computed(() => store.value)
</script>
在組件的模板或腳本中,使用插件添加到 Store 的自定義屬性。
// 在模板中
<p>Plugin Property: {{ store.myCustomProperty }}</p>
// 在腳本中
console.log(store.myCustomProperty) // 輸出: Hello from plugin!
pinia.use
方法注冊一個回調(diào),該回調(diào)接收每個 Store 并可以對其進行操作。.use()
方法注冊插件。defineStore
定義 Store,包括 state、getters、actions。記住這一點,開發(fā)者需要添加自定義邏輯和屬性,通過Pinia 插件系統(tǒng)就 OK。
Pinia 為 TypeScript 用戶提供了一流的支持,確保類型安全和開發(fā)體驗。Pinia 的 TypeScript 支持主要體現(xiàn)在以下幾個方面:
.d.ts
),確保 Pinia API 在 TypeScript 項目中的類型正確性。下面是一個使用 TypeScript 與 Pinia 結(jié)合使用的步驟解析,包括代碼示例:
確保你的項目已經(jīng)配置了 TypeScript,并且安裝了必要的類型聲明文件。
npm install typescript @vue/compiler-sfc
使用 TypeScript 的類型定義來創(chuàng)建 Pinia Store。
import { defineStore } from 'pinia'
interface State {
count: number
message: string
}
export const useMyStore = defineStore('myStore', {
state: (): State => ({
count: 0,
message: 'Hello, Pinia with TypeScript!'
}),
getters: {
// 使用 TypeScript 來聲明 getter 的返回類型
doubleCount: (state): number => state.count * 2
},
actions: {
increment(): void {
this.count++
}
}
})
在 Vue 組件中使用 Store,并利用 TypeScript 提供類型安全。
<template>
<div>
<p>Count: {{ count }}</p>
<p>Message: {{ message }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import useMyStore from '@/stores/myStore'
const store = useMyStore()
const count = computed(() => store.count)
const message = computed(() => store.message)
const doubleCount = computed(() => store.doubleCount)
function increment() {
store.increment()
}
</script>
使用類型守衛(wèi)來確保對 Store 屬性的訪問是安全的。
if (store.hasOwnProperty('count')) {
// TypeScript 知道 'count' 存在且為 number 類型
console.log(store.count)
}
interface
)來定義 Store 的狀態(tài)類型。defineStore
并傳入類型化的 state 函數(shù),以及聲明了返回類型的 getters 和 actions。hasOwnProperty
方法和類型守衛(wèi)來安全地訪問 Store 的屬性。小結(jié)一下,Pinia 與 TypeScript 結(jié)合使用可以提供類型安全的狀態(tài)管理,同時編輯器的自動補全和類型檢查功能可以提高開發(fā)效率和減少錯誤。
服務(wù)器端渲染(SSR)是一種將網(wǎng)站頁面在服務(wù)器上生成并發(fā)送給客戶端的技術(shù)。對于狀態(tài)管理庫來說,SSR 支持意味著可以在服務(wù)器上初始化和操作狀態(tài),然后將狀態(tài)序列化后發(fā)送到客戶端,客戶端再將這些狀態(tài)恢復(fù)以保持與服務(wù)器端相同的狀態(tài)。
Pinia 對 SSR 的支持主要體現(xiàn)在以下幾個方面:
下面是一個使用 Pinia 進行 SSR 的步驟解析,包括代碼示例:
首先,定義一個 Pinia Store。
import { defineStore } from 'pinia'
export const useMyStore = defineStore('myStore', {
state: () => ({
count: 0
}),
// 其他選項...
})
在服務(wù)器端,創(chuàng)建 Store 實例并初始化狀態(tài),然后將狀態(tài)序列化。
// server.js
import { createPinia } from 'pinia'
import useMyStore from '@/stores/myStore'
const pinia = createPinia()
// 模擬從數(shù)據(jù)庫獲取初始狀態(tài)
const initialState = { count: 10 }
// 創(chuàng)建 Store 實例并設(shè)置初始狀態(tài)
const store = useMyStore(pinia)
store.$state.count = initialState.count
// 序列化狀態(tài)
const stateToTransfer = JSON.stringify(store.$state)
在客戶端,接收服務(wù)器端發(fā)送的狀態(tài),然后恢復(fù)到對應(yīng)的 Store 中。
// client.js
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue' // Vue 應(yīng)用的根組件
import useMyStore from '@/stores/myStore'
const pinia = createPinia()
// 假設(shè)從服務(wù)器接收的狀態(tài)如下
const stateFromServer = JSON.parse(/* state serialized from server */)
// 創(chuàng)建 Store 實例并恢復(fù)狀態(tài)
const store = useMyStore(pinia)
store.$state.count = stateFromServer.count
const app = createApp(App)
app.use(pinia)
app.mount('#app')
在 Vue 組件中,像平常一樣使用 Store。
<template>
<div>{{ count }}</div>
</template>
<script setup>
import { computed } from 'vue'
import useMyStore from '@/stores/myStore'
const store = useMyStore()
const count = computed(() => store.count)
</script>
defineStore
定義一個 Pinia Store。JSON.stringify
序列化狀態(tài)。$state
中。一句話,Pinia支持SSR有助于提高應(yīng)用的初始加載性能和SEO優(yōu)化。
在 Pinia 中,映射輔助函數(shù)用于將 Store 中的狀態(tài)(state)、獲取器(getters)、動作(actions)映射到組件的計算屬性(computed properties)、方法(methods)或響應(yīng)式屬性上。這些輔助函數(shù)提供了一種便捷的方式來使用 Store,而無需在每個組件中重復(fù)編寫相同的代碼。
Pinia 的映射輔助函數(shù)主要包括:
mapState
:將 Store 中的狀態(tài)映射為組件的計算屬性。mapGetters
:將 Store 中的獲取器映射為組件的計算屬性。mapActions
:將 Store 中的動作映射為組件的方法。下面是一個使用 Pinia 映射輔助函數(shù)的步驟解析,包括代碼示例:
首先,定義一個 Pinia Store。
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', {
state: () => ({
items: []
}),
getters: {
itemCount: (state) => state.items.length
},
actions: {
addItem(item) {
this.items.push(item)
}
}
})
在組件中,使用 mapState
、mapGetters
和 mapActions
將 Store 的屬性映射到組件上。
<template>
<div>
<p>Item Count: {{ itemCount }}</p>
<button @click="addItem({ id: 1, name: 'Apple' })">Add Apple</button>
</div>
</template>
<script setup>
import { mapState, mapGetters, mapActions } from 'pinia'
import useCartStore from '@/stores/cart'
const store = useCartStore()
// 使用 mapState 映射狀態(tài)
const items = mapState(store, 'items')
// 使用 mapGetters 映射獲取器
const itemCount = mapGetters(store, 'itemCount')
// 使用 mapActions 映射動作
const { addItem } = mapActions(store, ['addItem'])
</script>
在組件的模板中,直接使用映射的計算屬性和方法。
<p>Item Count: {{ itemCount }}</p>
<button @click="addItem({ id: 1, name: 'Apple' })">Add Apple</button>
defineStore
定義一個包含狀態(tài)、獲取器和動作的 Pinia Store。mapState
:將 items
狀態(tài)映射為組件的計算屬性。mapGetters
:將 itemCount
獲取器映射為組件的計算屬性。mapActions
:將 addItem
動作映射為組件的方法。itemCount
來顯示項目數(shù)量,使用映射的方法 addItem
來添加新項目。使用映射輔助函數(shù),Pinia 可以讓開發(fā)者以聲明式的方式在組件中使用 Store 的狀態(tài)和行為,從而減少樣板代碼并提高組件的可讀性和可維護性。此外,這些輔助函數(shù)還有助于保持響應(yīng)性,確保當(dāng) Store 中的狀態(tài)變化時,組件能夠自動更新。
不得不說,Pinia 提供了一種靈活且高效的方式來管理 Vue 應(yīng)用的狀態(tài),無論是在單頁應(yīng)用還是服務(wù)器端渲染的場景下,都有出色的表現(xiàn),寫完收工,歡迎關(guān)注威哥愛編程,一起走全棧之路。
更多建議: