在 Vue.js 生態系統中,Pinia 是目前官方推薦的狀態管理函式庫。不過,當我們透過 Pinia 儲存資料時,這些資料究竟放在哪裡?本文拆解 Pinia 的運作原理,以及它如何處理資料儲存。

Pinia 的核心概念

Pinia 把應用程式的狀態切成獨立的「store」,每個 store 負責管理一塊特定的狀態。這種設計讓狀態管理更模組化,也比較容易維護。

定義一個 store 的基本寫法:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'guest',
}),
actions: {
increment() {
this.count++
},
},
})

記憶體中的資料儲存

Pinia 的資料主要放在應用程式的記憶體裡,底層是 Vue 的響應式系統。

  • 響應式物件:store 的 state 在底層是用 ref() 包裹、再透過 reactive() 展開。這讓所有依賴該狀態的元件在資料變動時自動重新渲染,不需要手動觸發。
  • JavaScript 執行環境:響應式物件存在 JavaScript 執行環境中——客戶端應用放在瀏覽器記憶體,伺服器端渲染(SSR)則放在伺服器記憶體,頁面重整或 tab 關閉後就消失。

持久化儲存

預設情況下 Pinia 不會把資料寫到任何持久化位置,頁面重整就歸零。需要跨重整保留資料時,有幾種做法:

1. localStorage / sessionStorage

直接在 action 裡手動讀寫,或用 pinia-plugin-persistedstate 自動化這個流程。

手動寫法

1
2
3
4
5
6
7
8
9
10
11
export const useUserStore = defineStore('user', {
state: () => ({
token: localStorage.getItem('token') ?? '',
}),
actions: {
setToken(token) {
this.token = token
localStorage.setItem('token', token)
},
},
})

使用 pinia-plugin-persistedstate

1
npm install pinia-plugin-persistedstate
1
2
3
4
5
6
// main.js
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
1
2
3
4
export const useUserStore = defineStore('user', {
state: () => ({ token: '', name: '' }),
persist: true, // 預設使用 localStorage,key 為 store id
})

也可以細調要持久化哪些欄位,或改用 sessionStorage

1
2
3
4
persist: {
storage: sessionStorage,
pick: ['token'], // 只持久化 token,忽略 name
}

2. IndexedDB

適合儲存量較大的客戶端資料(例如離線資料集)。需注意 IndexedDB 是非同步 API,無法直接插入 pinia-plugin-persistedstate(後者要求同步的 getItem/setItem 介面)。實務上通常透過 localForage 包一層同步介面來銜接。

3. 伺服器端儲存

需要跨裝置或跨工作階段保留的資料,應透過 API 寫入資料庫,Pinia 只扮演客戶端快取的角色。

資料流動過程

1
2
3
4
5
6
7
8
9
應用啟動
└─ Pinia 初始化各 store,載入初始 state 至記憶體

使用者操作
└─ 呼叫 action 或直接修改 state
└─ Vue 響應式系統偵測到變更,觸發元件重新渲染

(啟用持久化外掛時)
└─ 資料變更同步寫入 localStorage / sessionStorage

小結

Pinia 的狀態存在記憶體裡,以響應式物件的形式跟著 Vue 的渲染週期走。頁面重整就消失是預設行為,不是 bug——需要持久化時,pinia-plugin-persistedstate 是最直接的方案,配合 pick 選項可以只持久化必要的欄位,避免把敏感資料(如完整 token payload)無意間全寫進 localStorage。