在大多數(shù)情況下,state 都是你的 store 的核心。人們通常會先定義能代表他們 APP 的 state。在 Pinia 中,state 被定義為一個返回初始狀態(tài)的函數(shù)。這使得 Pinia 可以同時支持服務(wù)端和客戶端。
import { defineStore } from 'pinia'
const useStore = defineStore('storeId', {
// 為了完整類型推理,推薦使用箭頭函數(shù)
state: () => {
return {
// 所有這些屬性都將自動推斷出它們的類型
count: 0,
name: 'Eduardo',
isAdmin: true,
items: [],
hasChanged: true,
}
},
})
:::tip
如果你使用的是 Vue 2,你在 state
中創(chuàng)建的數(shù)據(jù)與 Vue 實例中的 data
遵循同樣的規(guī)則,即 state 對象必須是清晰的,當(dāng)你想向其添加新屬性時,你需要調(diào)用 Vue.set()
。參考:Vue#data。
:::
你并不需要做太多努力就能使你的 state 兼容 TS。確保啟用了 strict,或者至少啟用了 noImplicitThis,Pinia 將自動推斷您的狀態(tài)類型! 但是,在某些情況下,您應(yīng)該幫助它進行一些轉(zhuǎn)換:
const useStore = defineStore('storeId', {
state: () => {
return {
// 用于初始化空列表
userList: [] as UserInfo[],
// 用于尚未加載的數(shù)據(jù)
user: null as UserInfo | null,
}
},
})
interface UserInfo {
name: string
age: number
}
如果你愿意,你可以用一個接口定義 state,并添加 state()
的返回值的類型。
interface State {
userList: UserInfo[]
user: UserInfo | null
}
const useStore = defineStore('storeId', {
state: (): State => {
return {
userList: [],
user: null,
}
},
})
interface UserInfo {
name: string
age: number
}
state
默認(rèn)情況下,你可以通過 store
實例訪問 state,直接對其進行讀寫。
const store = useStore()
store.count++
使用選項式 API 時,你可以通過調(diào)用 store 的 $reset()
方法將 state 重置為初始值。
const store = useStore()
store.$reset()
<VueSchoolLink href="https://vueschool.io/lessons/access-pinia-state-in-the-options-api" title="Access Pinia State via the Options API" />
在下面的例子中,你可以假設(shè)相關(guān) store 已經(jīng)創(chuàng)建了:
// 示例文件路徑:
// ./src/stores/counter.js
import { defineStore } from 'pinia'
const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
})
如果你不能使用組合式 API,但你可以使用 computed
,methods
,...,那你可以使用 mapState()
輔助函數(shù)將 state 屬性映射為只讀的計算屬性:
import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counter'
export default {
computed: {
// 可以訪問組件中的 this.count
// 與從 store.count 中讀取的數(shù)據(jù)相同
...mapState(useCounterStore, ['count'])
// 與上述相同,但將其注冊為 this.myOwnName
...mapState(useCounterStore, {
myOwnName: 'count',
// 你也可以寫一個函數(shù)來獲得對 store 的訪問權(quán)
double: store => store.count * 2,
// 它可以訪問 `this`,但它沒有標(biāo)注類型...
magicValue(store) {
return store.someGetter + this.count + this.double
},
}),
},
}
如果你想修改這些 state 屬性 (例如,如果你有一個表單),你可以使用 mapWritableState()
作為代替。但注意你不能像 mapState()
那樣傳遞一個函數(shù):
import { mapWritableState } from 'pinia'
import { useCounterStore } from '../stores/counter'
export default {
computed: {
// 可以訪問組件中的 this.count,并允許設(shè)置它。
// this.count++
// 與從 store.count 中讀取的數(shù)據(jù)相同
...mapWritableState(useCounterStore, ['count'])
// 與上述相同,但將其注冊為 this.myOwnName
...mapWritableState(useCounterStore, {
myOwnName: 'count',
}),
},
}
:::tip
對于像數(shù)組這樣的集合,你并不一定需要使用 mapWritableState()
,mapState()
也允許你調(diào)用集合上的方法,除非你想用 cartItems = []
替換整個數(shù)組。
:::
<!-- TODO: disable this with strictMode
-->
除了用 store.count++
直接改變 store,你還可以調(diào)用 $patch
方法。它允許你用一個 state
的補丁對象在同一時間更改多個屬性:
store.$patch({
count: store.count + 1,
age: 120,
name: 'DIO',
})
不過,用這種語法的話,有些變更真的很難實現(xiàn)或者很耗時:任何集合的修改(例如,向數(shù)組中添加、移除一個元素或是做 splice
操作)都需要你創(chuàng)建一個新的集合。因此,$patch
方法也接受一個函數(shù)來組合這種難以用補丁對象實現(xiàn)的變更。
store.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})
<!-- TODO: disable this with strictMode
, { noDirectPatch: true }
-->
兩種變更 store 方法的主要區(qū)別是,$patch()
允許你將多個變更歸入 devtools 的同一個條目中。同時請注意,直接修改 state
,$patch()
也會出現(xiàn)在 devtools 中,而且可以進行 time travel (在 Vue 3 中還沒有)。
state
你不能完全替換掉 store 的 state,因為那樣會破壞其響應(yīng)性。但是,你可以 patch 它。
// 這實際上并沒有替換`$state`
store.$state = { count: 24 }
// 在它內(nèi)部調(diào)用 `$patch()`:
store.$patch({ count: 24 })
你也可以通過變更 pinia
實例的 state
來設(shè)置整個應(yīng)用的初始 state。這常用于 SSR 中的激活過程。
pinia.state.value = {}
類似于 Vuex 的 subscribe 方法,你可以通過 store 的 $subscribe()
方法偵聽 state 及其變化。比起普通的 watch()
,使用 $subscribe()
的好處是 subscriptions 在 patch 后只觸發(fā)一次 (例如,當(dāng)使用上面的函數(shù)版本時)。
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 和 cartStore.$id 一樣
mutation.storeId // 'cart'
// 只有 mutation.type === 'patch object'的情況下才可用
mutation.payload // 傳遞給 cartStore.$patch() 的補丁對象。
// 每當(dāng)狀態(tài)發(fā)生變化時,將整個 state 持久化到本地存儲。
localStorage.setItem('cart', JSON.stringify(state))
})
默認(rèn)情況下,state subscription 會被綁定到添加它們的組件上 (如果 store 在組件的 setup()
里面)。這意味著,當(dāng)該組件被卸載時,它們將被自動刪除。如果你想在組件卸載后依舊保留它們,請將 { detached: true }
作為第二個參數(shù),以將 state subscription 從當(dāng)前組件中分離:
<script setup>
const someStore = useSomeStore()
// 此訂閱器即便在組件卸載之后仍會被保留
someStore.$subscribe(callback, { detached: true })
</script>
:::tip
你可以在 pinia
實例上使用 watch()
函數(shù)偵聽整個 state。
watch(
pinia.state,
(state) => {
// 每當(dāng)狀態(tài)發(fā)生變化時,將整個 state 持久化到本地存儲。
localStorage.setItem('piniaState', JSON.stringify(state))
},
{ deep: true }
)
更多建議: