App下載

什么是Pinia? 為什么當你的Vue用上Pinia你就離不開它?

紫色的彩虹 2023-09-28 11:24:43 瀏覽數 (3119)
反饋

為什么你應該使用 Pinia?

微信截圖_20230928111904

Pinia 是 Vue 的專屬狀態(tài)管理庫,它允許你跨組件或頁面共享狀態(tài)。如果你熟悉組合式 API 的話,你可能會認為可以通過一行簡單的 export const state = reactive({}) 來共享一個全局狀態(tài)。對于單頁應用來說確實可以,但如果應用在服務器端渲染,這可能會使你的應用暴露出一些安全漏洞。 而如果使用 Pinia,即使在小型單頁應用中,你也可以獲得如下功能:

  • Devtools 支持追蹤 actions、mutations 的時間線在組件中展示它們所用到的 Store讓調試更容易的 Time travel
  • 熱更新不必重載頁面即可修改 Store開發(fā)時可保持當前的 State
  • 插件:可通過插件擴展 Pinia 功能
  • 為 JS 開發(fā)者提供適當的 TypeScript 支持以及自動補全功能。
  • 支持服務端渲染

為什么取名 Pinia?

?微信截圖_20230928111834

Pinia (發(fā)音為 /pi?nj?/,類似英文中的 “peenya”) 是最接近有效包名 pi?a (西班牙語中的 pineapple,即“菠蘿”) 的詞。 菠蘿花實際上是一組各自獨立的花朵,它們結合在一起,由此形成一個多重的水果。 與 Store 類似,每一個都是獨立誕生的,但最終它們都是相互聯(lián)系的。 它(菠蘿)也是一種原產于南美洲的美味熱帶水果。

對比 Vuex

?

Pinia 起源于一次探索 Vuex 下一個迭代的實驗,因此結合了 Vuex 5 核心團隊討論中的許多想法。最后,我們意識到 Pinia 已經實現了我們在 Vuex 5 中想要的大部分功能,所以決定將其作為新的推薦方案來代替 Vuex。

與 Vuex 相比,Pinia 不僅提供了一個更簡單的 API,也提供了符合組合式 API 風格的 API,最重要的是,搭配 TypeScript 一起使用時有非常可靠的類型推斷支持。

RFC?

最初,Pinia 沒有經過任何 RFC 的流程。我基于自己開發(fā)應用的經驗,同時通過閱讀其他人的代碼,為使用 Pinia 的用戶工作,以及在 Discord 上回答問題等方式驗證了一些想法。 這些經歷使我產出了這樣一個可用的解決方案,并適應了各種場景和應用規(guī)模。我會一直在保持其核心 API 不變的情況下發(fā)布新版本,同時不斷優(yōu)化本庫。

現在 Pinia 已經成為推薦的狀態(tài)管理解決方案,它和 Vue 生態(tài)系統(tǒng)中的其他核心庫一樣,都要經過 RFC 流程,它的 API 也已經進入穩(wěn)定狀態(tài)。

對比 Vuex 3.x/4.x?

Vuex 3.x 只適配 Vue 2,而 Vuex 4.x 是適配 Vue 3 的。

Pinia API 與 Vuex(<=4) 也有很多不同,即:

  • mutation 已被棄用。它們經常被認為是極其冗余的。它們初衷是帶來 devtools 的集成方案,但這已不再是一個問題了。
  • 無需要創(chuàng)建自定義的復雜包裝器來支持 TypeScript,一切都可標注類型,API 的設計方式是盡可能地利用 TS 類型推理。
  • 無過多的魔法字符串注入,只需要導入函數并調用它們,然后享受自動補全的樂趣就好。
  • 無需要動態(tài)添加 Store,它們默認都是動態(tài)的,甚至你可能都不會注意到這點。注意,你仍然可以在任何時候手動使用一個 Store 來注冊它,但因為它是自動的,所以你不需要擔心它。
  • 不再有嵌套結構的模塊。你仍然可以通過導入和使用另一個 Store 來隱含地嵌套 stores 空間。雖然 Pinia 從設計上提供的是一個扁平的結構,但仍然能夠在 Store 之間進行交叉組合。你甚至可以讓 Stores 有循環(huán)依賴關系。
  • 不再有可命名的模塊??紤]到 Store 的扁平架構,Store 的命名取決于它們的定義方式,你甚至可以說所有 Store 都應該命名。

基礎示例?

先創(chuàng)建一個 Store:

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

export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 0 }
  },
  // 也可以這樣定義
  // state: () => ({ count: 0 })
  actions: {
    increment() {
      this.count++
    },
  },
})

然后你就可以在一個組件中使用該 store 了:

<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
counter.count++
// 自動補全! ?
counter.$patch({ count: counter.count + 1 })
// 或使用 action 代替
counter.increment()
</script>
<template>
  <!-- 直接從 store 中訪問 state -->
  <div>Current Count: {{ counter.count }}</div>
</template>

為實現更多高級用法,你甚至可以使用一個函數 (與組件 setup() 類似) 來定義一個 Store:

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }

  return { count, increment }
})

如果你還不熟悉 setup() 函數和組合式 API,別擔心,Pinia 也提供了一組類似 Vuex 的 映射 state 的輔助函數。你可以用和之前一樣的方式來定義 Store,然后通過 mapStores()、mapState() 或 mapActions() 訪問:

const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

const useUserStore = defineStore('user', {
  // ...
})

export default defineComponent({
  computed: {
    // 其他計算屬性
    // ...
    // 允許訪問 this.counterStore 和 this.userStore
    ...mapStores(useCounterStore, useUserStore)
    // 允許讀取 this.count 和 this.double
    ...mapState(useCounterStore, ['count', 'double']),
  },
  methods: {
    // 允許讀取 this.increment()
    ...mapActions(useCounterStore, ['increment']),
  },
})

想學習更系統(tǒng)的學習Pinia, 請訪問《Pinia 中文教程》


0 人點贊