# Nuxt3 Pinia Plugin Persistedstate [Pinia Plugin Persistedstate](https://prazdevs.github.io/pinia-plugin-persistedstate/) 讓你可以將 Pinia 的狀態持久化儲存在瀏覽器的 Cookie、SessionStorage 或 LocalStorage 中。 --- ## 安裝 ```bash npm install pinia @pinia/nuxt pinia-plugin-persistedstate ``` --- ## Nuxt 配置 在 `nuxt.config.js` 中加入以下設置: ```javascript export default { modules: [ '@pinia/nuxt', 'pinia-plugin-persistedstate' ], imports: { // 設定要 auto imports 的資料夾 dirs: ['stores'] } } ``` ### 常見問題處理 **錯誤:** 在 Nuxt 3.7.0+ 版本可能出現 `[vite-node] [ERR_LOAD_URL]` 錯誤 :::spoiler 示意圖 ![截圖 2024-01-31 下午3.37.40](https://hackmd.io/_uploads/r1tyYuvcT.png) ::: **解決方式:** 在 `nuxt.config.js` 加入 transpile 配置 ```javascript export default { // ...其他配置 build: { transpile: ['pinia-plugin-persistedstate'] } } ``` **參考資料:** [GitHub Issue #236](https://github.com/prazdevs/pinia-plugin-persistedstate/issues/236) --- ## Store 定義 在 `/stores/` 目錄下建立 store 檔案: ```javascript import { defineStore } from 'pinia' // 方法一: Options API 語法 export const useCounterStore = defineStore('counter', { state: () => ({ message: 'Hello World!!' }), getters: { // getters 定義 }, actions: { setNewText(newText) { this.message = newText } }, persist: { storage: persistedState.sessionStorage } }) // 方法二: Composition API 語法 (推薦) export const useCounterStore = defineStore( 'counter', () => { // State const count = ref(0) const message = ref('pinia') const objUser = ref({ id: 1, name: 'Pinia' }) // Actions const addCount = () => { count.value++ } const setNewText = (newText) => { message.value = newText } return { count, message, objUser, addCount, setNewText } }, { // Persist 配置 persist: { key: 'counter-store', // 自訂儲存 key storage: process.client ? persistedState.sessionStorage : null, paths: ['count', 'message'] // 只儲存指定的狀態 } } ) ``` --- ## Persist 配置選項 ### 1. 基本啟用 ```javascript persist: true ``` **預設行為:** - 儲存位置: Cookie - 儲存 Key: Store 的 ID (defineStore 第一個參數) - 儲存範圍: 整個 state --- ### 2. 自訂 Key ```javascript persist: { key: 'my-custom-key' } ``` 覆寫預設的儲存 key 名稱。 --- ### 3. Storage 選項 ```javascript persist: { storage: persistedState.sessionStorage } ``` **可用選項:** - `persistedState.cookies` (預設) - `persistedState.localStorage` - `persistedState.sessionStorage` **重要提醒:** 為避免 SSR Hydration 錯誤,建議使用: :::spoiler error message Hydration completed but contains mismatches. ![截圖 2024-01-30 下午1.11.57](https://hackmd.io/_uploads/SkpCBWIq6.png) ::: ```javascript storage: process.client ? persistedState.sessionStorage : null ``` --- ### 4. Cookie 進階配置 ```javascript persist: { storage: persistedState.cookiesWithOptions({ sameSite: 'strict', maxAge: 60 * 60 * 24 * 7, // 7 天 secure: true, path: '/' }) } ``` 選項參考 [Nuxt useCookie 文檔](https://nuxt.com/docs/api/composables/use-cookie#options) --- ### 5. 部分狀態儲存 ```javascript persist: { paths: ['count', 'message'] } ``` 只儲存指定的狀態屬性,其他屬性不會被持久化。 --- ### 6. Debug 模式 ```javascript persist: { debug: true } ``` 啟用後會在 Console 輸出錯誤訊息,方便除錯。 --- ## 在組件中使用 ```htmlembedded <template> <div> <ClientOnly> <div> <p>{{ counter.message }}</p> <button @click="counter.setNewText('New text!')"> 更新文字 </button> </div> <div> <p>計數: {{ counter.count }}</p> <button @click="counter.addCount"> 增加計數 </button> </div> </ClientOnly> </div> </template> <script setup> const counter = useCounterStore() </script> ``` ### Hydration 錯誤處理 如果遇到 `Hydration completed but contains mismatches` 錯誤: **方法一:** 使用 `<ClientOnly>` 組件包裹使用 store 的部分 **方法二:** 修改 storage 配置 (推薦) ```javascript storage: process.client ? persistedState.sessionStorage : null ``` --- ## 參考資料 - [Pinia Plugin Persistedstate 官方文檔](https://prazdevs.github.io/pinia-plugin-persistedstate/) - [GitHub Issue #218 - Customize persist option](https://github.com/prazdevs/pinia-plugin-persistedstate/issues/218) - [Nuxt 3 ClientOnly 使用教學](https://ithelp.ithome.com.tw/articles/10327918) - [IT邦幫忙 - Pinia Plugin Persistedstate 教學](https://ithelp.ithome.com.tw/articles/10332698) --- ## 完整配置範例 ```javascript export const useUserStore = defineStore( 'user', () => { const userInfo = ref(null) const token = ref('') const preferences = ref({}) const login = (info) => { userInfo.value = info } const logout = () => { userInfo.value = null token.value = '' } return { userInfo, token, preferences, login, logout } }, { persist: { key: 'user-session', storage: process.client ? persistedState.localStorage : null, paths: ['userInfo', 'token'], // 不儲存 preferences debug: import.meta.env.DEV } } ) ``` --- > **提示:** 敏感資訊 (如 token) 建議加密後再儲存,或考慮使用 httpOnly Cookie。