最近連續看了幾個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)
}
最後,推薦大家嘗試,真的好用~