Vue3 Vite3 狀態管理 pinia 基本使用、持久化、在路由守衛中的使用

2022-10-10 18:03:14

《基於 vite 建立 vue3 專案》一文中整合了 pinia,有不少夥伴不知道 pinia 是什麼,本文簡單介紹 pinia。主要包括三方面:

  1. pinia 的基本用法,在《基於 vite 建立 vue3 專案》中 demo 的基礎上簡單重構。
  2. 如何持久化 pinia 中的資料,保證瀏覽器重新整理時,pinia 中的資料不丟失;
  3. vue-router 路由守衛中如何使用 pinia

文中的 demo 仍然基於 vite

1 pinia 的使用

1.1 pinia 是什麼

在 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,上面演示了 keystorage 屬性,該物件的其他屬性如下:

}
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;
}

3 在路由守衛中使用狀態

前面演示了在元件中使用 pinia,在元件外如何使用呢?這裡演示在全域性路由守衛中獲取狀態值。咱們建立一個路由守衛,在路由守衛中使用 nprogress 顯示頁面載入進度條。

3.1 建立全域性路由守衛

  1. 安裝 nprogress
yarn add nprogress
yarn add @types/nprogress -D 
  1. 建立全域性路由守衛

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)
})
  1. main.ts 中引入全域性路由守衛:
...
import '@/router/guard/index'
...

此時路由切換時,頁面頂部會出現載入進度條,路由切換完成時該進度條消失。如果效果不明顯,可在前置守衛中 setTimeout 檢視效果(個人覺得沒這必要,畫蛇添足):

// 全域性前置守衛
router.beforeEach((to, from) => {
  nProgress.start()
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(true)
    }, 1000)
  })
})

3.2 全域性守衛中使用全域性狀態

實際開發中,路由切換時,可能需要從全域性狀態中獲取 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 失效了。那應該怎麼處理呢?

3.3 正確的處理方式

上面這種傳遞 pinia 物件給 useDemoStore() 函數只是一種野路子,pinia 官網已經清楚寫明元件外應該如何使用 pinia:

image-20221009160056386

在勾點函數外,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-archetypemain 分支可以直接 yarn dev 啟動執行; template 分支是 yyg-cli 執行 yyg create 建立專案時拉取的模板。你也可以先執行 npm install -g yyg-cli 安裝 yyg-cli 腳手架工具,然後通過 yyg create xxx 建立專案,建立後的專案包含了 vue3 vite 的全部demo。