輕量級狀態管理庫Pinia試吃

hexiaobang發表於2021-08-25

  最近連續看了幾個GitHub上的開源專案,裡面都用到了 Pinia 這個狀態管理庫,於是研究了一下,發現確實是個好東西!那麼,Pinia 的特點:

  • 輕量化 —— Pinia 體積約1KB,十分輕巧,載入執行都很快速,相對來說Vuex明顯塊頭更大一些
  • Pinia 的 API 設計並非追求另闢蹊徑,事實上它十分接近 Vuex 5的提案,並且對composition API非常友好(作者本身是Vue.js的核心成員,並且積極參與Vue Router以及Vuex的API設計)
  • 模組化設計,支援建立數個store(有點類似Vuex中的module),在打包時會被自動拆分
  • 跨模組呼叫十分直觀方便,你可以在任意的 store 之間交叉組合使用
  • 完美支援Typescript,不得不說這個是Vuex的最大劣勢之一,深度使用過Vuex + Ts的童鞋應該都懂
  • 可脫離元件使用,比如在編寫router時呼叫
  • Vue devtools相容(暫不支援個別功能點)
  • 同時支援Vue2/Vue3

  具體的使用細節就不照搬文件了,官網連結奉上 => Pinia
  值得一提的是,雖然文件使用英文編寫,但是文件結構、語言組織等方面都屬上乘,通俗易懂

  下面介紹幾個亮點特性。

一、結構設計與Vuex API非常相似

這使得它學習成本很低,如果之前使用過Vuex的話,可以非常方便的上手

import { defineStore } from 'pinia'

interface AppState {
  name: string;
}

export const useUserStore = defineStore({
  id: 'app',
  state: (): AppState =>({
    name: 'Eduardo'
  }),
  getters: {},
  actions: {}
})

二、取消mutation,使用三種方式更改state

<script lang="ts" setup>
import { useStore } from '@/store/modules/app';
import { ref } from 'vue';

const store = useStore();

function raise() {
  // 方式一:直接修改 -> 'direct'
  store.salary += 10000;

  // 方式二:patch物件 -> 'patch object',填入打算更改的state欄位即可
  store.$patch({
    salary: store.salary + 10000,
  });

  // 方式三:patch函式 -> 'patch function',可鍵入語句,執行復雜邏輯
  store.$patch((state) => {
    state.salary += 10000;
  });
}
</script>

三、store使用reactive包裹,自帶響應性

無需二次包裹reactive、computed,即可在模板中直接使用,同時具有響應性

<template>
  <div>{{ store.name }}, 文章閱讀量:{{ store.article }}</div>
  <hr />
  <button @click="jump">點選跳轉閱讀新文章</button>
</template>

四、跨模組呼叫直觀方便

直接引入其他store的hook函式,呼叫即可

import { useSomeOtherStore } from './auth-store'

export const useAppStore = defineStore('app', {
  state: () => ({
    // ...
  }),
  actions: {
    async someAction() {
      const someOtherStore = useSomeOtherStore()
      // ...
    },
  },
})

五、元件外呼叫

使用者建立的類似useStore這樣的Hook函式預設會自動注入在單頁應用入口處建立並傳遞給app例項的Pinia例項(有點拗口...),在組建內使用時,Pinia例項肯定已經傳遞給app例項了,這時候直接呼叫hook函式使用就完事了。但在元件外使用時,很可能出現在呼叫hook函式時,Pinia例項還未傳遞給app例項,這時候就需要使用者手動將Pinia例項傳入hook函式,並暴露出來以供呼叫。具體可參見官方文件的對應章節 -> Using a store outside of a component

// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { pinia } from '@/store'

const app = createApp(App)
// 將pinia例項傳遞給app例項,在此之後使用useStore鉤子,會自動注入pinia例項,否則需要在useStore中手動注入
app.use(pinia)

app.mount('#app')

// store/index.ts
import { createPinia } from 'pinia'

const pinia = createPinia()
export { pinia }

// store/modules/app.ts
import { defineStore } from 'pinia'
import { pinia } from '@/store'

interface AppState {
  name: string;
}

export const useUserStore = defineStore({
  id: 'app',
  state: (): AppState =>({
    name: 'Eduardo'
  }),
  getters: {},
  actions: {}
})

// 這裡的pinia即為通過createPinia()生成並在入口處傳遞給app例項的pinia例項,在此手動注入,並暴露
export function useAppStoreHook() {
  return useAppStore(store)
}

最後,推薦大家嘗試,真的好用~

相關文章