在《基於 vite 建立 vue3 專案》一文中整合了 pinia,有不少夥伴不知道 pinia 是什麼,本文簡單介紹 pinia。主要包括三方面:
文中的 demo 仍然基於 vite
在 vue 2.x 中,vuex 是官方的狀態管理庫,並且 vue 3 中也有對應的 vuex 版本。但 vue 作者尤大神看了 pinia 後,強勢推薦使用 pinia 作為狀態管理庫。下圖是 vue 官網 「生態系統」,pinia 是 vue 生態之一。
如果需要將其儲存在 sessionStorage 中,就需要設定 persist 的值為一個物件:
...
const useDemoStore = defineStore('demo', () => {
...
}, {
persist: {
key: 'aaa',
storage: sessionStorage
}
})
此時狀態就會同步快取到 sessionStorage 中,並且key 為咱們指定的 key:
persist 物件型別為 PersistedStateOptions,上面演示了 key 和 storage 屬性,該物件的其他屬性如下:
}
interface PersistedStateOptions {
/**
* Storage key to use.
* @default $store.id
*/
key?: string;
/**
* Where to store persisted state.
* @default localStorage
*/
storage?: StorageLike;
/**
* Dot-notation paths to partially save state. Saves everything if undefined.
* @default undefined
*/
paths?: Array<string>;
/**
* Customer serializer to serialize/deserialize state.
*/
serializer?: Serializer;
/**
* Hook called before state is hydrated from storage.
* @default null
*/
beforeRestore?: (context: PiniaPluginContext) => void;
/**
* Hook called after state is hydrated from storage.
* @default undefined
*/
afterRestore?: (context: PiniaPluginContext) => void;
}
前面演示了在元件中使用 pinia,在元件外如何使用呢?這裡演示在全域性路由守衛中獲取狀態值。咱們建立一個路由守衛,在路由守衛中使用 nprogress 顯示頁面載入進度條。
yarn add nprogress
yarn add @types/nprogress -D
src/router/guard/index.ts:
import router from '@/router'
import nProgress from 'nprogress'
import 'nprogress/nprogress.css'
nProgress.configure({
showSpinner: false
})
// 全域性前置守衛
router.beforeEach((to, from) => {
nProgress.start()
return true
})
// 全域性後置勾點
router.afterEach(() => {
nProgress.done(true)
})
...
import '@/router/guard/index'
...
此時路由切換時,頁面頂部會出現載入進度條,路由切換完成時該進度條消失。如果效果不明顯,可在前置守衛中 setTimeout 檢視效果(個人覺得沒這必要,畫蛇添足):
// 全域性前置守衛
router.beforeEach((to, from) => {
nProgress.start()
return new Promise(resolve => {
setTimeout(() => {
resolve(true)
}, 1000)
})
})
實際開發中,路由切換時,可能需要從全域性狀態中獲取 token 等資訊,判斷是否能進入下一個頁面。這裡演示路由切換時獲取 demo 中的 counter 的值。
首先試試在勾點函數外面使用全域性狀態:
...
import useDemoStore from '@/store/modules/demo'
import { storeToRefs } from 'pinia'
...
const demoStore = useDemoStore()
const { counter } = storeToRefs(demoStore)
// 全域性前置守衛
router.beforeEach((to, from) => {
nProgress.start()
// 從 store 中獲取其他值,再決定返回值
// 這裡演示獲取 store 中 counter 的值
console.log(`counter:${counter}`)
return true
})
...
此時瀏覽器控制檯會報如下錯誤,這是因為 pinia 還沒有掛載到 app 上。
網上有些解決方案是直接範例化一個 pinia 範例,傳遞給 useDemoStore() 函數,如下:
...
import useDemoStore from '@/store/modules/demo'
import { storeToRefs } from 'pinia'
import pinia from '@/store'
...
const demoStore = useDemoStore(pinia)
const { counter } = storeToRefs(demoStore)
...
這樣做,瀏覽器控制檯不報錯了,頁面也可以正常載入,路由切換時,控制檯會輸出當前 counter 的值。
但是如果重新整理瀏覽器,counter 的值又被初始化為 0,貌似前面設定的持久化外掛 pinia-plugin-persistedstate 失效了。那應該怎麼處理呢?
上面這種傳遞 pinia 物件給 useDemoStore() 函數只是一種野路子,pinia 官網已經清楚寫明元件外應該如何使用 pinia:
在勾點函數外,pinia 還沒有被掛載,但是在前置守衛函數中,pinia 已經被掛載了,所以獲取全域性狀態,需要在勾點函數中進行,正確的實現如下:
import router from '@/router'
import nProgress from 'nprogress'
import 'nprogress/nprogress.css'
import useDemoStore from '@/store/modules/demo'
import { storeToRefs } from 'pinia'
nProgress.configure({
showSpinner: false
})
// 全域性前置守衛
router.beforeEach((to, from) => {
nProgress.start()
const demoStore = useDemoStore()
const { counter } = storeToRefs(demoStore)
// 從 store 中獲取其他值,再決定返回值
// 這裡演示獲取 store 中 counter 的值
console.log(`counter:${counter.value}`)
return true
})
// 全域性後置勾點
router.afterEach(() => {
nProgress.done(true)
})
文中 demo 在 github 上搜尋 vue3-vite-archetype,main 分支可以直接 yarn dev 啟動執行; template 分支是 yyg-cli 執行 yyg create 建立專案時拉取的模板。你也可以先執行 npm install -g yyg-cli 安裝 yyg-cli 腳手架工具,然後通過 yyg create xxx 建立專案,建立後的專案包含了 vue3 vite 的全部demo。