Pinia State

2023-09-28 11:38 更新

State

在大多數(shù)情況下,state 都是你的 store 的核心。人們通常會先定義能代表他們 APP 的 state。在 Pinia 中,state 被定義為一個返回初始狀態(tài)的函數(shù)。這使得 Pinia 可以同時支持服務(wù)端和客戶端。

  1. import { defineStore } from 'pinia'
  2. const useStore = defineStore('storeId', {
  3. // 為了完整類型推理,推薦使用箭頭函數(shù)
  4. state: () => {
  5. return {
  6. // 所有這些屬性都將自動推斷出它們的類型
  7. count: 0,
  8. name: 'Eduardo',
  9. isAdmin: true,
  10. items: [],
  11. hasChanged: true,
  12. }
  13. },
  14. })

:::tip 如果你使用的是 Vue 2,你在 state 中創(chuàng)建的數(shù)據(jù)與 Vue 實例中的 data 遵循同樣的規(guī)則,即 state 對象必須是清晰的,當你想向其添加新屬性時,你需要調(diào)用 Vue.set() 。參考:Vue#data。 :::

TypeScript

你并不需要做太多努力就能使你的 state 兼容 TS。確保啟用了 strict,或者至少啟用了 noImplicitThis,Pinia 將自動推斷您的狀態(tài)類型! 但是,在某些情況下,您應(yīng)該幫助它進行一些轉(zhuǎn)換:

  1. const useStore = defineStore('storeId', {
  2. state: () => {
  3. return {
  4. // 用于初始化空列表
  5. userList: [] as UserInfo[],
  6. // 用于尚未加載的數(shù)據(jù)
  7. user: null as UserInfo | null,
  8. }
  9. },
  10. })
  11. interface UserInfo {
  12. name: string
  13. age: number
  14. }

如果你愿意,你可以用一個接口定義 state,并添加 state() 的返回值的類型。

  1. interface State {
  2. userList: UserInfo[]
  3. user: UserInfo | null
  4. }
  5. const useStore = defineStore('storeId', {
  6. state: (): State => {
  7. return {
  8. userList: [],
  9. user: null,
  10. }
  11. },
  12. })
  13. interface UserInfo {
  14. name: string
  15. age: number
  16. }

訪問 state

默認情況下,你可以通過 store 實例訪問 state,直接對其進行讀寫。

  1. const store = useStore()
  2. store.count++

重置 state

使用選項式 API 時,你可以通過調(diào)用 store 的 $reset() 方法將 state 重置為初始值。

  1. const store = useStore()
  2. store.$reset()

使用選項式 API 的用法

<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)建了:

  1. // 示例文件路徑:
  2. // ./src/stores/counter.js
  3. import { defineStore } from 'pinia'
  4. const useCounterStore = defineStore('counter', {
  5. state: () => ({
  6. count: 0,
  7. }),
  8. })

如果你不能使用組合式 API,但你可以使用 computed,methods,...,那你可以使用 mapState() 輔助函數(shù)將 state 屬性映射為只讀的計算屬性:

  1. import { mapState } from 'pinia'
  2. import { useCounterStore } from '../stores/counter'
  3. export default {
  4. computed: {
  5. // 可以訪問組件中的 this.count
  6. // 與從 store.count 中讀取的數(shù)據(jù)相同
  7. ...mapState(useCounterStore, ['count'])
  8. // 與上述相同,但將其注冊為 this.myOwnName
  9. ...mapState(useCounterStore, {
  10. myOwnName: 'count',
  11. // 你也可以寫一個函數(shù)來獲得對 store 的訪問權(quán)
  12. double: store => store.count * 2,
  13. // 它可以訪問 `this`,但它沒有標注類型...
  14. magicValue(store) {
  15. return store.someGetter + this.count + this.double
  16. },
  17. }),
  18. },
  19. }

可修改的 state

如果你想修改這些 state 屬性 (例如,如果你有一個表單),你可以使用 mapWritableState() 作為代替。但注意你不能像 mapState() 那樣傳遞一個函數(shù):

  1. import { mapWritableState } from 'pinia'
  2. import { useCounterStore } from '../stores/counter'
  3. export default {
  4. computed: {
  5. // 可以訪問組件中的 this.count,并允許設(shè)置它。
  6. // this.count++
  7. // 與從 store.count 中讀取的數(shù)據(jù)相同
  8. ...mapWritableState(useCounterStore, ['count'])
  9. // 與上述相同,但將其注冊為 this.myOwnName
  10. ...mapWritableState(useCounterStore, {
  11. myOwnName: 'count',
  12. }),
  13. },
  14. }

:::tip 對于像數(shù)組這樣的集合,你并不一定需要使用 mapWritableState(),mapState() 也允許你調(diào)用集合上的方法,除非你想用 cartItems = [] 替換整個數(shù)組。 :::

變更 state

<!-- TODO: disable this with strictMode -->

除了用 store.count++ 直接改變 store,你還可以調(diào)用 $patch 方法。它允許你用一個 state 的補丁對象在同一時間更改多個屬性:

  1. store.$patch({
  2. count: store.count + 1,
  3. age: 120,
  4. name: 'DIO',
  5. })

不過,用這種語法的話,有些變更真的很難實現(xiàn)或者很耗時:任何集合的修改(例如,向數(shù)組中添加、移除一個元素或是做 splice 操作)都需要你創(chuàng)建一個新的集合。因此,$patch 方法也接受一個函數(shù)來組合這種難以用補丁對象實現(xiàn)的變更。

  1. store.$patch((state) => {
  2. state.items.push({ name: 'shoes', quantity: 1 })
  3. state.hasChanged = true
  4. })

<!-- 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 它。

  1. // 這實際上并沒有替換`$state`
  2. store.$state = { count: 24 }
  3. // 在它內(nèi)部調(diào)用 `$patch()`:
  4. store.$patch({ count: 24 })

你也可以通過變更 pinia 實例的 state 來設(shè)置整個應(yīng)用的初始 state。這常用于 SSR 中的激活過程。

  1. pinia.state.value = {}

訂閱 state

類似于 Vuex 的 subscribe 方法,你可以通過 store 的 $subscribe() 方法偵聽 state 及其變化。比起普通的 watch(),使用 $subscribe() 的好處是 subscriptionspatch 后只觸發(fā)一次 (例如,當使用上面的函數(shù)版本時)。

  1. cartStore.$subscribe((mutation, state) => {
  2. // import { MutationType } from 'pinia'
  3. mutation.type // 'direct' | 'patch object' | 'patch function'
  4. // 和 cartStore.$id 一樣
  5. mutation.storeId // 'cart'
  6. // 只有 mutation.type === 'patch object'的情況下才可用
  7. mutation.payload // 傳遞給 cartStore.$patch() 的補丁對象。
  8. // 每當狀態(tài)發(fā)生變化時,將整個 state 持久化到本地存儲。
  9. localStorage.setItem('cart', JSON.stringify(state))
  10. })

默認情況下,state subscription 會被綁定到添加它們的組件上 (如果 store 在組件的 setup() 里面)。這意味著,當該組件被卸載時,它們將被自動刪除。如果你想在組件卸載后依舊保留它們,請將 { detached: true } 作為第二個參數(shù),以將 state subscription 從當前組件中分離

  1. <script setup>
  2. const someStore = useSomeStore()
  3. // 此訂閱器即便在組件卸載之后仍會被保留
  4. someStore.$subscribe(callback, { detached: true })
  5. </script>

:::tip 你可以在 pinia 實例上使用 watch() 函數(shù)偵聽整個 state。

  1. watch(
  2. pinia.state,
  3. (state) => {
  4. // 每當狀態(tài)發(fā)生變化時,將整個 state 持久化到本地存儲。
  5. localStorage.setItem('piniaState', JSON.stringify(state))
  6. },
  7. { deep: true }
  8. )
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號