兩分鐘上手 pinia

安裝

官網

yarn add pinia
# or with npm
npm install pinia

初始化 Pinia

import { createPinia } from 'pinia'

const pinia = createPinia()
pinia.use(SomePiniaPlugin) // 給 pinia 裝插件

const app = createApp(App)
app.use(pinia)

這裏需要注意時間順序:只有在調用 app.use(pinia) 之後才能調用 useXxxStore()

使用 Store

注意

  1. defineStore 接受一個 id,不同數據源的 id 必須是不同的
  2. 不能將 useCounter() 的返回值解構,這會導致數據響應式的丟失

寫法一:

更像原先的 vuex

// src/stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counterStore', {
  state: ()=> {
    return {j: 0, k: 0}
  }
})

// Counter.vue
import { useCounterStore } from 'path/to/src/stores/counterStore'

export default {
  setup() {
    const counterStore = useCounterStore()
    // TODO 默認情況下可以直接這麼更改,但是不推薦 // https://pinia.vuejs.org/core-concepts/state.html#accessing-the-state
    counterStore.j ++
    
    // 這裏在視圖裏使用 counterStore.j 和 counterStore.k
    // 但你不能解構 counterStore,只能像下面這樣解構:
    const { j, k } = storeToRefs(counterStore) // 注意:這裏會自動忽略 方法 和 非響應式數據(Creates an object of references with all the state, getters, and plugin-added state properties of the store. Similar to toRefs() but specifically designed for Pinia stores so methods and non reactive properties are completely ignored.)
    return {
      counterStore, j, k,
    }
  },
}
Store Getters

getters 其實就是 store 的計算屬性集合,而且 getter 不能是異步函數

export const useStore = defineStore('main', {
  state: () => ({
    counter: 0,
  }),
  getters: {
    doubleCount(state) {
      return state.counter * 2
    },
    doublePlusOne() {
      return this.doubleCount + 1 // getter 訪問另一個 getter 或者 state 可以用 this
    },
    getUserById: (state) => { // getter 可以返回一個函數,不過這會導致緩存失效
      return (userId) => state.users.find((user) => user.id === userId)
    },
    otherGetter(state) { // 你還可以調用其他的 store
      const otherStore = useOtherStore()
      return state.localData + otherStore.data
    },
  },
})
// store.doubleCount 和 store.doublePlusOne 就可以直接當做屬性使用了
// store.getUserById(userId) 可以當做函數使用
Store Actions

action 其實就是 store 的 methods,而且可以是異步函數

export const useUserStore = defineStore('users', {
  state: () => ({
    userData: null,
  }),
  actions: {
    async getUser(token) {
      this.userData = await api.post({ token })
    },
  },
})
// 然後你就可以使用 userStore.getUser(token) 了

寫法二:

推薦這種,符合Vue3 setup的編程模式,讓結構更加扁平化

import { ref, computed } from 'vue';
import { defineStore } from 'pinia';

export const useUserStore = defineStore('users', () => {
  const userData= ref({});
  const getUser = async () => {
    userData.value = await api.post({ token })
  }
  
  const userName = computed(() => userData.value.name)

  return { userData, userName, getUser };
});

store.$patch(object | fn)

批量更新

counterStore.$patch(
   { name: 'pinia', age: counterStore.age + 1 } 
)

cartStore.$patch((state) => {
  state.items.push({ name: 'vuex', age: 18 })
  state.hasChanged = true
})

store.$subscribe(fn)

用於監聽 state 的整體變化。

cartStore.$subscribe((mutation, state) => {
  // import { MutationType } from 'pinia'
  mutation.type // 'direct' | 'patch object' | 'patch function'
  mutation.storeId  
  mutation.payload // 獲取 $patch 接收到的參數

  localStorage.setItem('cart', JSON.stringify(state))
})

它有一個很方便的特性是會自動在組件卸載時註銷,如果你不想要,可以在 $subscribe 第二個參數處傳入 {detached: true} 選項。

你也可以使用 watch 達到類似的效果:

watch(
  pinia.state,
  (state) => {
    localStorage.setItem('piniaState', JSON.stringify(state))
  },
  { deep: true }
)

store.$onAction()

用於監控所有 action 的執行情況。

const unsubscribe = someStore.$onAction(
  ({
    name, // action 的名字
    store, // store === someStore
    args, // action 的實際參數
    after, // action 成功之後執行 after
    onError, // action 失敗之後執行 onError
  }) => {
    const startTime = Date.now()
    console.log(`開始執行 "${name}" 參數爲 [${args.join(', ')}].`)
    after((result) => {
      console.log(
        `執行成功 "${name}" 用時 ${Date.now() - startTime}毫秒\n結果爲:${result}`
      )
    })
    onError((error) => {
      console.warn(
        `執行失敗 "${name}" 用時 ${Date.now() - startTime}毫秒\n報錯爲:${error}.`
      )
    })
  }
)
// $onAction 會在它所在組件卸載時自動銷燬
// 如果你將 $onAction 的第二個參數設置爲 true,那麼你需要自己調用 unsubscribe 來取消監聽。

store.$reset()

你可以使用 counterStore.$reset() 重置 state

store.$state

// 下面兩句代碼都能覆蓋原有 state
store.$state = { counter: 666, name: 'Paimon' }
pinia.state.value = {} // 這句常用在 SSR
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章